【速查手册】TypeScript 高级类型 cheat sheet
学习 TypeScript 到一定阶段,必须要学会高阶类型的使用,否则一些复杂的场景若是用 any 类型来处理的话,也就失去了 TS 类型检查的意义。本文罗列了 TypeScript 常用的高阶类型,包含 官方 、以及 常用的非官方 的高级类型声明,该手册直接硬啃的话有些枯燥,适合平时快速查阅,使用 Ctrl+F 来查找关键词来定位即可。官方文档 - 高级类型:优先阅读,建议阅读英文文档。附 中文文档,有人做了专门的读书笔记 Typescript学习记录:高级类型TypeScript: Built-in generic types:推荐,用案例详细解释高阶类型的使用;TS 一些工具泛型的使用及其实现:TS 内置工具泛型高阶使用TypeScript 2.1 新特性一览:查找/映射类型及 any 类型的推断 都是在 2.1 版本引入的TypeScript 2.8:Exclude 等条件类型是在 2.8 版本引入的,附中文 TypeScript 2.8 引入条件类型lib.es2015.d.ts:大部分的声明在这个文件中可以找到TypeScript 强大的类型别名:行文结构比较合理,也比较完善,可以当手册来查1、基础1.1、交叉类型交叉类型是将 多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。就是说这个类型的对象同时拥有了这三种类型的成员示例:extend 融合方法function extend<T, U>(first: T, second: U): T & U { let result = <T & U>{}; for (let id in first) { (<any>result)[id] = (<any>first)[id]; } for (let id in second) { if (!result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result;}特殊情况:T | never = TT & never = never (which #16446 provides)1.2、extends 关键字T extends U ? X : Y表示,如果 T 可以赋值给 U (类型兼容),则返回 X,否则返回 Y;1.3、使用 keyof 和 inkeyof 可以用来取得一个对象接口的所有 key 值:interface Foo { name: string; age: number}type T = keyof Foo // -> “name” | “age"而 in 则可以遍历枚举类型, 例如:type Keys = “a” | “b"type Obj = { [p in Keys]: any} // -> { a: any, b: any }keyof 产生联合类型, in 则可以遍历枚举类型, 所以他们经常一起使用。1.4、infer 关键字infer 这个关键字是在 TS 2.8 版本引入的, 在条件类型语句中,该关键字用于替代手动获取类型。TypeScript 为此提供了一个示例,他们创建了一个叫作 Flatten 的类型,用于将数组转成他们需要的元素类型:type Flatten<T> = T extends any[] ? T[number] : T;如果使用关键字 infer 就可以将上面的代码简化成:type Flatten<T> = T extends Array<infer U> ? U : T;2、映射类型2.1、Partial(官方)作用:将传入的属性变为可选项源码:type Partial<T> = { [P in keyof T]?: T[P] };解释:keyof T 拿到 T 所有属性名然后 in 进行遍历, 将值赋给 P, 最后 T[P] 取得相应属性的值.结合中间的 ? 我们就明白了 Partial 的含义了.扩展:内置的 Partial 有个局限性,就是只支持处理第一层的属性,如果是嵌套多层的就没有效果了,不过可以如下自定义:type PowerPartial<T> = { // 如果是 object,则递归类型 [U in keyof T]?: T[U] extends object ? PowerPartial<T[U]> : T[U]};2.2、Required(官方)作用:将传入的属性变为必选项源码:type Required<T> = { [P in keyof T]-?: T[P] };解释:我们发现一个有意思的用法 -?, 这里很好理解就是将可选项代表的 ? 去掉, 从而让这个类型变成必选项与之对应的还有个 +? , 这个含义自然与 -? 之前相反, 它是用来把属性变成可选项的2.3、Readonly(官方)作用:将传入的属性变为只读选项源码:type Readonly<T> = { readonly [P in keyof T]: T[P] };扩展:在 巧用 Typescript 中,作者创建了 DeepReadonly 的声明,使用 递归 的思想让任何子属性都不可更改type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]>;}const a = { foo: { bar: 22 } }const b = a as DeepReadonly<typeof a>b.foo.bar = 33 // Hey, stop!2.4、Mutable(第三方)作用:将 T 的所有属性的 readonly 移除源码:type Mutable<T> = { -readonly [P in keyof T]: T[P]}解释:这一对加减符号操作符 + 和 -, 进行的不是变量的之间的进行加减而是对 readonly 属性进行加减2.5、Record(官方)作用:将 K 中所有的属性的值转化为 T 类型源码:type Record<K extends keyof any, T> = { [P in K]: T };示例:// 对所有 T 类型的属性 K, 将它转换为 Ufunction mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>;const names = { foo: “hello”, bar: “world”, baz: “bye” };const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number }2.6、Pick(官方)作用:从 T 中取出 一系列 K 的属性源码:type Pick<T, K extends keyof T> = { [P in K]: T[P] };示例:// 从 T 挑选一些属性 Kdeclare function pick<T, K extends keyof T>(obj: T, …keys: K[]): Pick<T, K>;const nameAndAgeOnly = pick(person, “name”, “age”); // { name: string, age: number }3、条件类型3.1、Exclude(官方)某些地方也称为 Diff作用:从 T 中剔除可以赋值给 U 的类型,换言之就是从T 中排除 U源码:type Exclude<T, U> = T extends U ? never : T;解释:在 ts 2.8 中引入了一个条件类型, T extends U ? X : Y 表示如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y对于联合类型来说会自动分发条件,例如 T extends U ? X : Y, T 可能是 A | B 的联合类型, 那实际情况就变成(A extends U ? X : Y) | (B extends U ? X : Y)示例:type T = Exclude<1 | 2, 1 | 3> // -> 2参考文档:Add support for literal type subtractionTypeScript在React高阶组件中的使用技巧3.2、Extract(官方)作用:从 T 中提取出包含在 U 的类型,换言之就是从T 中提取出 U 子集源码:type Extract<T, U> = T extends U ? T : never;示例:type T = Extract<1 | 2, 1 | 3> // -> 13.3、Omit (第三方)作用:从 T 中忽略在 K 中的属性名 ,实现忽略对象某些属性功能,多在高阶组件中使用源码:type Omit<T, K> = Pick<T, Exclude<keyof T, K>>示例:type Foo = Omit<{name: string, age: number}, ’name’> // -> { age: number }3.4、Overwrite(第三方)作用: T 中的定义被在 K 中的内容所覆盖,多在高阶组件中使用,内部借助 Diff 操作实现源码:type Overwrite<T, U> = { [P in Exclude<keyof T, keyof U>]: T[P] } & U;示例:type Item1 = { a: string, b: number, c: boolean };type Item2 = { a: number };type T3 = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };3.5、ReturnType (官方)作用:从 T 中忽略在 K 中的属性名 ,实现忽略对象某些属性功能,多在高阶组件中使用源码:type ReturnType<T> = T extends ( …args: any[]) => infer R ? R : any;解释:我们可以用 infer 声明一个类型变量,是用它获取函数的返回类型,简单说就是用它取到函数返回值的类型方便之后使用.示例:function foo(x: number): Array<number> { return [x];}type fn = ReturnType<typeof foo>;4、函数相关4.1、ThisType(官方)作用:用于指定上下文对象类型的源码:// node_modules/typescript/lib/lib.es5.d.tsinterface ThisType<T> { }解释:可以看到声明中只有一个接口,没有任何的实现说明这个类型是在 TS 源码层面支持的,而不是通过类型变换。示例:interface Person { name: string; age: number;}const obj: ThisType<Person> = { dosth() { this.name // string }}这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了4.2、InstanceType(官方)作用:用于获取构造函数类型的实例类型源码:// node_modules/typescript/lib/lib.es5.d.tstype InstanceType<T extends new (…args: any[]) => any> = T extends new (…args: any[]) => infer R ? R : any;解释:使用 infer 和 extends 条件判断完成示例:class C { x = 0; y = 0;}type T20 = InstanceType<typeof C>; // Ctype T21 = InstanceType<any>; // anytype T22 = InstanceType<never>; // anytype T23 = InstanceType<string>; // Errortype T24 = InstanceType<Function>; // Error这样的话,就可以指定 obj 里的所有方法里的上下文对象改成 Person 这个类型了4.3、NonNullable(官方)作用:这个类型可以用来过滤类型中的 null 及 undefined 类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype NonNullable<T> = T extends null | undefined ? never : T;解释:使用 extends 条件判断完成示例:type T22 = string | number | null;type T23 = NonNullable<T22>; // -> string | number;4.4、Parameters(官方)作用:该类型可以获得函数的参数类型组成的元组类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype Parameters<T extends (…args: any[]) => any> = T extends (…args: infer P) => any ? P : never;解释:使用 infer 和 extends 条件判断完成示例:function foo(x: number): Array<number> { return [x];}type P = Parameters<typeof foo>; // -> [number]此时 P 的真实类型就是 foo 的参数组成的元组类型 [number]4.5、ConstructorParameters(官方)作用:获得类的参数类型组成的元组类型。源码:// node_modules/typescript/lib/lib.es5.d.tstype ConstructorParameters<T extends new (…args: any[]) => any> = T extends new (…args: infer P) => any ? P : never;解释:使用 infer 和 extends 条件判断完成示例:class Person { private firstName: string; private lastName: string; constructor(firstName: string, lastName: string) { this.firstName = firstName; this.lastName = lastName; }}type P = ConstructorParameters<typeof Person>; // -> [string, string]此时 P 就是 Person 中 constructor 的参数 firstName 和 lastName 的类型所组成的元组类型 [string, string]。 ...