当你不想重复自己时,有时一种类型需要基于另一种类型。
¥When you don’t want to repeat yourself, sometimes a type needs to be based on another type.
映射类型建立在索引签名的语法之上,用于声明未提前声明的属性类型:
¥Mapped types build on the syntax for index signatures, which are used to declare the types of properties which have not been declared ahead of time:
tsTrytypeOnlyBoolsAndHorses = {[key : string]: boolean |Horse ;};constconforms :OnlyBoolsAndHorses = {del : true,rodney : false,};
映射类型是一种泛型类型,它使用 PropertyKey 的联合(经常创建的 通过 keyof)来迭代键来创建类型:
¥A mapped type is a generic type which uses a union of PropertyKeys (frequently created via a keyof) to iterate through keys to create a type:
tsTrytypeOptionsFlags <Type > = {[Property in keyofType ]: boolean;};
在此示例中,OptionsFlags 将获取 Type 类型的所有属性并将其值更改为布尔值。
¥In this example, OptionsFlags will take all the properties from the type Type and change their values to be a boolean.
tsTrytypeFeatures = {darkMode : () => void;newUserProfile : () => void;};typeFeatureOptions =OptionsFlags <Features >;
映射修饰符
¥Mapping Modifiers
在映射期间可以应用两个额外的修饰符:readonly 和 ? 分别影响可变性和可选性。
¥There are two additional modifiers which can be applied during mapping: readonly and ? which affect mutability and optionality respectively.
你可以通过添加前缀 - 或 + 来移除或添加这些修饰符。如果你不添加前缀,则假定为 +。
¥You can remove or add these modifiers by prefixing with - or +. If you don’t add a prefix, then + is assumed.
tsTry// Removes 'readonly' attributes from a type's propertiestypeCreateMutable <Type > = {-readonly [Property in keyofType ]:Type [Property ];};typeLockedAccount = {readonlyid : string;readonlyname : string;};typeUnlockedAccount =CreateMutable <LockedAccount >;
tsTry// Removes 'optional' attributes from a type's propertiestypeConcrete <Type > = {[Property in keyofType ]-?:Type [Property ];};typeMaybeUser = {id : string;name ?: string;age ?: number;};typeUser =Concrete <MaybeUser >;
通过 as 重新映射键
¥Key Remapping via as
在 TypeScript 4.1 及更高版本中,你可以使用映射类型中的 as 子句重新映射映射类型中的键:
¥In TypeScript 4.1 and onwards, you can re-map keys in mapped types with an as clause in a mapped type:
tstype MappedTypeWithNewProperties<Type> = {[Properties in keyof Type as NewKeyType]: Type[Properties]}
你可以利用 模板字面类型 等功能从以前的属性名称中创建新的属性名称:
¥You can leverage features like template literal types to create new property names from prior ones:
tsTrytypeGetters <Type > = {[Property in keyofType as `get${Capitalize <string &Property >}`]: () =>Type [Property ]};interfacePerson {name : string;age : number;location : string;}typeLazyPerson =Getters <Person >;
你可以通过条件类型生成 never 来过滤掉键:
¥You can filter out keys by producing never via a conditional type:
tsTry// Remove the 'kind' propertytypeRemoveKindField <Type > = {[Property in keyofType asExclude <Property , "kind">]:Type [Property ]};interfaceCircle {kind : "circle";radius : number;}typeKindlessCircle =RemoveKindField <Circle >;
你可以映射任意联合,不仅是 string | number | symbol 的联合,还可以映射任何类型的联合:
¥You can map over arbitrary unions, not just unions of string | number | symbol, but unions of any type:
tsTrytypeEventConfig <Events extends {kind : string }> = {[E inEvents asE ["kind"]]: (event :E ) => void;}typeSquareEvent = {kind : "square",x : number,y : number };typeCircleEvent = {kind : "circle",radius : number };typeConfig =EventConfig <SquareEvent |CircleEvent >
进一步探索
¥Further Exploration
映射类型与此类型操作部分中的其他功能配合得很好,例如这里是 使用条件类型的映射类型,它返回 true 或 false,具体取决于对象是否将属性 pii 设置为字面 true:
¥Mapped types work well with other features in this type manipulation section, for example here is a mapped type using a conditional type which returns either a true or false depending on whether an object has the property pii set to the literal true:
tsTrytypeExtractPII <Type > = {[Property in keyofType ]:Type [Property ] extends {pii : true } ? true : false;};typeDBFields = {id : {format : "incrementing" };name : {type : string;pii : true };};typeObjectsNeedingGDPRDeletion =ExtractPII <DBFields >;