一,前言

本篇介绍TS的另一个高级类型-映射类型

二,映射类型

映射类型:

TS允许将一个类型映射成另外一个类型

将一个接口的所有属性映射为只读:

// 定义接口Obj
interface Obj {
    a: number
    b: string
    c: boolean
}
// 使用类型别名定义类型ReadonlyObj
type ReadonlyObj = Readonly<Obj>    // Readonly是TS内置的泛型接口

ReadonlyObj与Obj成员完全相同,区别是ReadonlyObj中的成员属性均为只读

typescript 对象属性 typescript object object_泛型接口


三,映射类型的实现原理

TS的内置类库
刚刚说了,Readonly是TS内置的泛型接口,查看TS源码(TS内置的类库)

node_module/typescript/lib/lib.es5.d.ts:

typescript 对象属性 typescript object object_泛型接口_02

Readonly的实现原理:
从源码可以看出Readonly是一个可索引类型的泛型接口

1)索引签名为P in keyof T :
其中keyof T就是一个一个索引类型的查询操作符,表示类型T所有属性的联合类型

2)P in :
相当于执行了一个for in操作,会把变量P依次绑定到T的所有属性上

3)索引签名的返回值就是一个索引访问操作符 : T[P]  这里代表属性P所指定的类型

4)最后再加上Readonly就把所有的属性变成了只读,这就是Readonly的实现原理
TS还预置了很多其他的映射类型:

如:将一个接口的所有属性变成可选的Partial映射类型

type PartialObj = Partial<Obj>  // 可选

typescript 对象属性 typescript object object_typescript 对象属性_03


源码:

typescript 对象属性 typescript object object_实现原理_04


可以发现:

可选和只读映射类型的实现几乎一样,知识属性变为可选

可以抽取对象子集的Pick映射类型:

type PickObj = Pick<Obj, 'a' | 'b'> // 抽取接口Obj中的属性a和b,形成新类型

新类型中仅包含指定属性a和b:

typescript 对象属性 typescript object object_操作符_05


源码:

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

Pick映射类型的实现原理:

Pick映射类型有两个参数:
第一个参数T,表示要抽取的目标对象
第二个参数K,具有一个约束:K一定要来自T所有属性字面量的联合类型,
即映射得到的新类型的属性一定要从K中选取

以上三种映射类型官方称为同态,意思是只作用于obj属性而不会引入新的属性

会引入新属性的非同态映射类型

会创建新属性的映射类型Record

type RecordObj = Record<'x' | 'y', Obj>
第一个参数是预定义的新属性,比如x,y
第二个参数就是已知类型

typescript 对象属性 typescript object object_泛型接口_06


映射出的新类型所具有的属性由Record的第一个属性指定

而这些属性类型为第二个参数指定的已知类型

这种类型就是一个非同态的类型

Record映射类型源码:

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

映射类型本质上是一种预先定义的泛型接口,
通常还会结合索引类型,获取对象的属性和属性值,
从而将一个对象映射为我们想要的结构,即目标类型


四,结尾

本篇介绍了TS的又一种高级类型-映射类型
通过Readonly(只读),Partial(可选),Pick(抽取),三个同态映射类型
和非同态映射类型Record(录制)介绍了映射类型使用和实现原理
在TS的内置类库中还有更多映射类型,可以去尝试