TypeScript 的映射类型 Mapped types (e.g. { [P in K]: T[P] })

在JS中咱们能够经过 for...in 遍历出一个 object{} 的全部 key 而后进行一些逻辑处理,那么在 TS 中是否有相似的功能用于遍历 interface{} ,在 TS2.1 版本就推出了此能力。git

它得出的结果只能赋值给类型,最简单的书写格式:github

{ [ K in P ] : T }
复制代码

下面咱们对其中的 KPT 各自表示什么,都进行详细的说明。函数

利用映射类型快速建立类型

type Coord = {
    [K in 'x' | 'y']: number;
};
// 获得
// type Coord = {
// x: number;
// y: number;
// }
复制代码

首选肯定它执行了一个循环(能够理解为相似 for...in 的效果),这里的 P 直接设置为 'x' | 'y' 的一个联合类型,而 K 是一个标识符,它映射为 P 的每个子类型。T 为数据类型,咱们直接固定为 number,也能够是任何其余复杂类型。ui

由于 T 值数据类型能够是任何值,甚至数值 1 也能够,所以咱们把数据类设成 K 自身也行:spa

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 中的声明彻底同样,能够简写为:code

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 是有效的值。ip

而 keyof 的一个特性: keyof T的类型会被认为是 stringnumbersymbol 的子类型。(关于 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;
// };

复制代码
相关文章
相关标签/搜索