当你不想重复自己时,有时一种类型需要基于另一种类型。
¥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:
tsTry
typeOnlyBoolsAndHorses = {[key : string]: boolean |Horse ;};constconforms :OnlyBoolsAndHorses = {del : true,rodney : false,};
映射类型是一种泛型类型,它使用 PropertyKey
的联合(经常创建的 通过 keyof
)来迭代键来创建类型:
¥A mapped type is a generic type which uses a union of PropertyKey
s (frequently created via a keyof
) to iterate through keys to create a type:
tsTry
typeOptionsFlags <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.
tsTry
typeFeatures = {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:
ts
type 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:
tsTry
typeGetters <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:
tsTry
typeEventConfig <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
:
tsTry
typeExtractPII <Type > = {[Property in keyofType ]:Type [Property ] extends {pii : true } ? true : false;};typeDBFields = {id : {format : "incrementing" };name : {type : string;pii : true };};typeObjectsNeedingGDPRDeletion =ExtractPII <DBFields >;