Mapped Types 映射转换类型
When you don’t want to repeat yourself, sometimes a type needs to be based on another type.
当你不想重复自己的时候,有时一种 type 需要基于另一种 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:
Mapped types(映射类型)基于index signatures(索引签名)的语法构建,索引签名用于声明尚未提前声明的属性类型·
type OnlyBoolsAndHorses = {
[key: string]: boolean | Horse;
};
const conforms: OnlyBoolsAndHorses = {
del: true,
rodney: false,
};
Try
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:
映射类型是一种泛型类型,它使用a union of PropertyKeys属性键的联合类型(通常通过keyof创建)来迭代键以创建类型
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
Try
In this example, OptionsFlags will take all the properties from the type Type and change their values to be a boolean.
在本例中,OptionsFlags将从泛型类型Type中获取所有属性,并将其值更改为boolean布尔值。
type FeatureFlags = {
darkMode: () => void;
newUserProfile: () => void;
};
type FeatureOptions = OptionsFlags<FeatureFlags>;
type FeatureOptions = {
darkMode: boolean;
newUserProfile: boolean;
}
Try
Mapping Modifiers
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.
有两个附加的修改器可以在映射过程中应用:readonly and ?(只读和?)分别影响可变性和可选性。
您可以通过在这些修饰符前面加- or +(-或+),来删除或添加这些修饰符。如果不添加前缀,则假设为+。
// 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;
}
Try
// 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;
}
Try
Key Remapping viaas
In TypeScript 4.1 and onwards, you can re-map keys in mapped types with an as clause in a mapped type:
在TypeScript 4.1及以后的版本中,您可以在映射类型中使用as子句重新映射映射类型中的键
type MappedTypeWithNewProperties<Type> = {
[Properties in keyof Type as NewKeyType]: Type[Properties]
}
其他地方都是Property,这里为啥是Properties? 待查考
NewKeyType 指代自定义的类型名
Type[Properties] 类型的值
You can leverage(利用) features like template literal types to create new property names from prior ones:
您可以利用模板文字类型等功能,从以前的属性名称创建新的属性名称
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;
}
Try
You can filter out keys by producing never via a conditional type:
您可以通过条件类型生成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;
}
Try
You can map over arbitrary(任意) unions, not just unions of string | number | symbol, but unions of any type:
你可以映射任意并集/联合类型,不仅仅是string | number | symbol的并集,还可以映射任何类型的并集
type EventConfig<Events extends { kind: string }> = {
[E in Events as E["kind"]]: (event: E) => void;
}
type SquareEvent = { kind: "square", x: number, y: number };
type CircleEvent = { kind: "circle", radius: number };
type Config = EventConfig<SquareEvent | CircleEvent>
type Config = {
square: (event: SquareEvent) => void;
circle: (event: CircleEvent) => void;
}
Try
Further Exploration 进一步探索
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:
映射类型与此类型操作部分中的其他功能配合得很好,例如,这里有一个使用条件类型的映射类型,该类型 根据对象是否拥有属性pii的true or false来决定设置字面量true
extends { 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;
}