在JS中我们可以通过 for...in
遍历出一个 object{}
的所有 key 然后进行一些逻辑处理,那么在 TS 中是否有类似的功能用于遍历 interface{}
,在 TS2.1 版本就推出了此能力。
它得出的结果只能赋值给类型,最简单的书写格式:
{ [ K in P ] : T }
下面我们对其中的 K
、P
、T
各自表示什么,都进行详细的说明。
利用映射类型快速创建类型
type Coord = {
[K in 'x' | 'y']: number;
};
// 得到
// type Coord = {
// x: number;
// y: number;
// }
首选确定它执行了一个循环(可以理解为类似 for...in 的效果),这里的 P 直接设置为 'x' | 'y'
的一个联合类型,而 K 是一个标识符,它映射为 P 的每一个子类型。T 为数据类型,我们直接固定为 number,也可以是任何其他复杂类型。
因为 T 值数据类型可以是任何值,甚至数值 1 也可以,因此我们把数据类设成 K 自身也行:
type Coord = { [K in 'x' | 'y']: K };
// 得到 type Coord = { x: 'x'; y: 'y'; }
利用映射类型进行复制
type Item = {
a: string
b: number
c: boolean
}
// Item 的所有属性的一个 联合类型
type ItemKeys = 'a' | 'b' | 'c';
// 也可以简写为:
// type ItemKeys = keyof Item;
type Copy = { [K in ItemKeys]: Item[K] };
// 得到 type Copy = { a: string, b: number, c: boolean };
这里的 Copy 类型与 Item 中的声明完全一样,可以简写为:
type Item = { a: string, b: number, c: boolean };
type Copy = { [K in keyof Item]: Item[K] };
// 得到 type Copy = { a: string, b: number, c: boolean };
基于此特性再结合 索引访问类型,我们可以封装出一个复制效果的函数类型:
type Copy<P> = { [K in keyof P]: P[K] }
type Item = { a: string, b: number, c: boolean };
type ItemCopy = Copy<Item>;
// 得到 type ItemCopy = { a: string, b: number, c: boolean };
生成类型的函数类型
基于此特性封装一个快速生成接口类型的函数类型:
type Create<P extends keyof any, T> = { [K in P]: T };
type Coord = Create<'x' | 'y', number>;
// 得到 type Coord = { x: number, y: number };
如果你很眼熟,则说明你见过或用过官方预置的高级类型 Record<K extends keyof any, T>
,是的,他们一模一样。
这里的 extends
条件类型用于继承的作用,此时我们传入的 联合类型。因 P
必须可分配给 string
类型的原因,我们需要进行一些限制,确保接收到的 P 是有效的值。
而 keyof 的一个特性: keyof T
的类型会被认为是 string
、number
、symbol
的子类型。(关于 keyof 的更多特性之后将讲解)基于 keyof any
的检测,因此下面的使用都会报错:
type Val = Create<true, boolean>;
// Error: Type 'true' does not satisfy the constraint 'string | number | symbol'.
type Val2 = Create<() => void>;
// Error: Type '() => void' does not satisfy the constraint 'string | number | symbol'
映射类型的装饰符
还可以使用 readonly
或 ?
对属性进行设置:
type Coord = {
readonly [K in 'x' | 'y']: number
};
// 得到
// type Coord = {
// readonly x: number;
// readonly y: number;
// };
两个装饰符也可组合使用:
type Coord = {
readonly [K in 'x' | 'y']?: number;
};
// 得到
// type Coord = {
// readonly x?: number;
// readonly y?: number;
// };
但这里面也有局限性,无法去除属性上已经存在的 装饰符:
type CoordOptional = {
x?: number;
readonly y: number;
};
type Coord = {
[K in keyof CoordOptional]: number;
};
// 得到
// type Coord = {
// x?: number;
// readonly y: number;
// };
因为这个原因,社区又在 TS2.8 又对其进行了完善,可以在上面的装饰符添加 -
或 +
符号:
type CoordOptional = {
x?: number;
readonly y: number;
};
type Coord = {
-readonly [K in keyof CoordOptional] -?: number;
};
// 得到
// type Coord = {
// x: number;
// y: number;
// };