TS 有个很是好用的功能就是类型别名。javascript
类型别名会给一个类型起个新名字。类型别名有时和接口很像,可是能够做用于原始值,联合类型,元组以及其它任何你须要手写的类型。html
使用类型别名能够实现不少复杂的类型,不少复杂的类型别名都须要借助关键字,咱们先来了解一下几个经常使用的关键字:java
extends
能够用来继承一个类,也能够用来继承一个 interface
,但还能够用来判断有条件类型:node
T extends U ? X : Y; 复制代码
上面的类型意思是,若 T
可以赋值给 U
,那么类型是 X
,不然为 Y
。git
原理是令 T'
和 U'
分别为 T
和 U
的实例,并将全部类型参数替换为 any
,若是 T'
能赋值给 U'
,则将有条件的类型解析成 X
,不然为Y
。github
上面的官方解释有点绕,下面举个栗子:typescript
type Words = 'a'|'b'|"c"; type W<T> = T extends Words ? true : false; type WA = W<'a'>; // -> true type WD = W<'d'>; // -> false 复制代码
a
能够赋值给 Words
类型,因此 WA
为 true
,而 d
不能赋值给 Words
类型,因此 WD
为 false
。数组
在 JS 中 typeof
能够判断一个变量的基础数据类型,在 TS 中,它还有一个做用,就是获取一个变量的声明类型,若是不存在,则获取该类型的推论类型。markdown
举两个栗子:函数
interface Person { name: string; age: number; location?: string; } const jack: Person = { name: 'jack', age: 100 }; type Jack = typeof jack; // -> Person function foo(x: number): Array<number> { return [x]; } type F = typeof foo; // -> (x: number) => number[] 复制代码
Jack
这个类型别名实际上就是 jack
的类型 Person
,而 F
的类型就是 TS 本身推导出来的 foo
的类型 (x: number) => number[]
。
keyof
能够用来取得一个对象接口的全部 key 值:
interface Person { name: string; age: number; location?: string; } type K1 = keyof Person; // "name" | "age" | "location" type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ... type K3 = keyof { [x: string]: Person }; // string | number 复制代码
in
能够遍历枚举类型:
type Keys = "a" | "b" type Obj = { [p in Keys]: any } // -> { a: any, b: any } 复制代码
上面 in
遍历 Keys
,并为每一个值赋予 any
类型。
在条件类型语句中, 能够用 infer
声明一个类型变量而且对它进行使用,
咱们能够用它获取函数的返回类型, 源码以下:
type ReturnType<T> = T extends ( ...args: any[] ) => infer R ? R : any; 复制代码
其实这里的 infer R
就是声明一个变量来承载传入函数签名的返回值类型, 简单说就是用它取到函数返回值的类型方便以后使用。
下面咱们看一下 TS 内置的一些类型别名:
Partial
的做用就是能够将某个类型里的属性所有变为可选项 ?
。
源码:
// node_modules/typescript/lib/lib.es5.d.ts type Partial<T> = { [P in keyof T]?: T[P]; }; 复制代码
从源码能够看到 keyof T
拿到 T
全部属性名, 而后 in
进行遍历, 将值赋给 P
, 最后 T[P]
取得相应属性的值. 结合中间的 ?
,将全部属性变为可选.
Required
的做用恰好跟 Partial
相反,Partial
是将全部属性改为可选项,Required
则是将全部类型改为必选项,源码以下:
// node_modules/typescript/lib/lib.es5.d.ts type Required<T> = { [P in keyof T]-?: T[P]; }; 复制代码
其中 -?
是表明移除 ?
这个 modifier 的标识。
与之对应的还有个 +?
, 这个含义天然与 -?
以前相反, 它是用来把属性变成可选项的,+
可省略,见 Partial
。
再拓展一下,除了能够应用于 ?
这个 modifiers ,还有应用在 readonly
,好比 Readonly
.
这个类型的做用是将传入的属性变为只读选项。
// node_modules/typescript/lib/lib.es5.d.ts type Readonly<T> = { readonly [P in keyof T]: T[P]; }; 复制代码
给子属性添加 readonly
的标识,若是将上面的 readonly
改为 -readonly
, 就是移除子属性的 readonly
标识。
这个类型则能够将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
源码实现以下:
// node_modules/typescript/lib/lib.es5.d.ts type Pick<T, K extends keyof T> = { [P in K]: T[P]; }; 复制代码
从源码能够看到 K
必须是 T
的 key,而后用 in
进行遍历, 将值赋给 P
, 最后 T[P]
取得相应属性的值。
该类型能够将 K
中全部的属性的值转化为 T
类型,源码实现以下:
// node_modules/typescript/lib/lib.es5.d.ts type Record<K extends keyof any, T> = { [P in K]: T; }; 复制代码
能够根据 K
中的全部可能值来设置 key,以及 value 的类型,举个例子:
type T11 = Record<'a' | 'b' | 'c', Person>; // -> { a: Person; b: Person; c: Person; } 复制代码
Exclude
将某个类型中属于另外一个的类型移除掉。
源码的实现:
// node_modules/typescript/lib/lib.es5.d.ts type Exclude<T, U> = T extends U ? never : T; 复制代码
以上语句的意思就是 若是 T
能赋值给 U
类型的话,那么就会返回 never
类型,不然返回 T
,最终结果是将 T
中的某些属于 U
的类型移除掉,举个例子:
type T00 = Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // -> 'b' | 'd' 复制代码
能够看到 T
是 'a' | 'b' | 'c' | 'd'
,而后 U
是 'a' | 'c' | 'f'
,返回的新类型就能够将 U
中的类型给移除掉,也就是 'b' | 'd'
了。
Extract
的做用是提取出 T
包含在 U
中的元素,换种更加贴近语义的说法就是从 T
中提取出 U
,源码以下:
// node_modules/typescript/lib/lib.es5.d.ts type Extract<T, U> = T extends U ? T : never; 复制代码
以上语句的意思就是 若是 T
能赋值给 U
类型的话,那么就会返回 T
类型,不然返回 never
,最终结果是将 T
和 U
中共有的属性提取出来,举个例子:
type T01 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'c' | 'f'>; // -> 'a' | 'c' 复制代码
能够看到 T
是 'a' | 'b' | 'c' | 'd'
,而后 U
是 'a' | 'c' | 'f'
,返回的新类型就能够将 T
和 U
中共有的属性提取出来,也就是 'a' | 'c'
了。
该类型的做用是获取函数的返回类型。
源码的实现
// node_modules/typescript/lib/lib.es5.d.ts type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any; 复制代码
实际使用的话,就能够经过 ReturnType
拿到函数的返回类型,以下的示例:
function foo(x: number): Array<number> { return [x]; } type fn = ReturnType<typeof foo>; // -> number[] 复制代码
这个类型是用于指定上下文对象类型的。
// node_modules/typescript/lib/lib.es5.d.ts interface ThisType<T> { } 复制代码
能够看到声明中只有一个接口,没有任何的实现,说明这个类型是在 TS 源码层面支持的,而不是经过类型变换。
这类型怎么用呢,举个例子:
interface Person { name: string; age: number; } const obj: ThisType<Person> = { dosth() { this.name // string } } 复制代码
这样的话,就能够指定 obj
里的全部方法里的上下文对象改为 Person
这个类型了。
该类型的做用是获取构造函数类型的实例类型。
源码实现:
// node_modules/typescript/lib/lib.es5.d.ts type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any; 复制代码
看一下官方的例子:
class C { x = 0; y = 0; } type T20 = InstanceType<typeof C>; // C type T21 = InstanceType<any>; // any type T22 = InstanceType<never>; // any type T23 = InstanceType<string>; // Error type T24 = InstanceType<Function>; // Error 复制代码
这个类型能够用来过滤类型中的 null
及 undefined
类型。
源码实现:
// node_modules/typescript/lib/lib.es5.d.ts type NonNullable<T> = T extends null | undefined ? never : T; 复制代码
好比:
type T22 = string | number | null; type T23 = NonNullable<T22>; // -> string | number; 复制代码
该类型能够得到函数的参数类型组成的元组类型。
源码实现:
// node_modules/typescript/lib/lib.es5.d.ts type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never; 复制代码
举个栗子:
function foo(x: number): Array<number> { return [x]; } type P = Parameters<typeof foo>; // -> [number] 复制代码
此时 P
的真实类型就是 foo
的参数组成的元组类型 [number]
。
该类型的做用是得到类的参数类型组成的元组类型,源码:
// node_modules/typescript/lib/lib.es5.d.ts type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never; 复制代码
举个栗子:
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]
。
下面是一些可能会常常用到,可是 TS 没有内置的一些类型别名:
有时候咱们想要继承某个接口,可是又须要在新接口中将某个属性给 overwrite 掉,这时候经过 Pick
和 Exclude
就能够组合出来 Omit
,用来忽略对象某些属性功能:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; // 使用 type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number } 复制代码
将 T 的全部属性的 readonly
移除:
type Mutable<T> = { -readonly [P in keyof T]: T[P] } 复制代码
内置的 Partial 有个局限性,就是只支持处理第一层的属性,若是是嵌套多层的就没有效果了,不过能够以下自定义:
type PowerPartial<T> = { // 若是是 object,则递归类型 [U in keyof T]?: T[U] extends object ? PowerPartial<T[U]> : T[U] }; 复制代码
相同的属性名称,但使值是一个 Promise
,而不是一个具体的值:
type Deferred<T> = { [P in keyof T]: Promise<T[P]>; }; 复制代码
为 T
的属性添加代理
type Proxify<T> = { [P in keyof T]: { get(): T[P]; set(v: T[P]): void } }; 复制代码
若有疑问,欢迎斧正!