类型转换是TS最好玩也是语言的灵魂,想玩好需要熟练各种手段和工具,下面一一介绍类型转换的一些常用手段。
有时候对象属性类型重复或者一个类型基于另一个了类型的时候
映射类型基于索引签名的语法构建,用于声明尚未提前声明的属性类型:
type OnlyBoolsAndHorses = {
[key: string]: boolean | Horse;
};
const conforms: OnlyBoolsAndHorses = {
del: true,
rodney: false,
};
泛型映射类型使用keyof 得到联合类型,通过key 迭代创建新类型
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
在这个例子里,OptionsFlags
将会把Type
所有的属性当做key,所有的值类型变成boolean
type FeatureFlags = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<FeatureFlags>;
//type FeatureOptions = {
//darkMode: boolean;
// newUserProfile: boolean;
//}
有两个附加的修饰符可以在映射期间应用:readonly和?分别影响可变性和可选性。
可以通过在前面加上-或+,删除或添加这些修饰符。如果不添加前缀,则假定为+。
// Removes 'readonly' attributes from a type's properties
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
type LockedAccount = {
readonly id: string;
readonly name: string;
};
type UnlockedAccount = CreateMutable<LockedAccount>;
//type UnlockedAccount = {
//id: string;
//name: string;
//}
// Removes 'optional' attributes from a type's properties
type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};
type MaybeUser = {
id: string;
name?: string;
age?: number;
};
type User = Concrete<MaybeUser>;
//type User = {
//id: string;
//name: string;
//age: number;
//}
在TypeScript 4.1及更高版本中,您可以使用映射类型中的as子句重新映射映射映射类型中的键:
type MappedTypeWithNewProperties<Type> = {
[Properties in keyof Type as NewKeyType]: Type[Properties]
}
您可以利用模板文字类型等功能从以前的属性名称创建新的属性名称:
type Getters<Type> = {
[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
//type LazyPerson = {
//getName: () => string;
//getAge: () => number;
//getLocation: () => string;
//}
您可以通过条件类型生成never来筛选出键:
// Remove the 'kind' property
type RemoveKindField<Type> = {
[Property in keyof Type as Exclude<Property, "kind">]: Type[Property]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
//type KindlessCircle = {
//radius: number;
//}
映射类型与此类型操作部分中的其他功能配合得很好,例如,这里有一个使用条件类型的映射类型,该类型根据对象的属性pii是否设置为文本true返回true或false:
type ExtractPII<Type> = {
[Property in keyof Type]: Type[Property] extends { pii: true } ? true : false;
};
type DBFields = {
id: { format: "incrementing" };
name: { type: string; pii: true };
};
type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields>;
//type ObjectsNeedingGDPRDeletion = {
//id: false;
//name: true;
//}