type A = Awaited<Promise<string>>; // A 的类型为 string
type B = Awaited<Promise<Promise<number>>>; // B 的类型为 number
type C = Awaited<boolean | Promise<number>>; // C 的类型为 boolean | number
Record
Record在 TS 中的源码实现:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
它用来生成一个属性为 K,类型为 T 的类型集合。如下所示,我用它生成了一个Foo类型,那么就表示所有指定为Foo类型的变量都必须包含一个 key 为a,value 为string类型的字段。否则,TS 类型检查器就会报错。
在 TypeScript 中默认内置了很多工具泛型,能够合理灵活的使用这些工具,可以使我们的类型定义更加灵活,严谨。下面是我根据自己的理解以及日常使用做的一些总结。
这些泛型定义在
node_modules/typescript/lib/lib.es5.d.ts
文件中,大概是从 1424 行开始。所以,有兴趣也可以研究一下它们的源码实现,很有意思。内置类型定义
Awaited
从名称来看,类似于
async
中的await
或者是 Promise 上的then
方法。它的作用就是可以递归的将 Promise 进行展开,以返回其最终类型:Record
Record
在 TS 中的源码实现:它用来生成一个属性为 K,类型为 T 的类型集合。如下所示,我用它生成了一个
Foo
类型,那么就表示所有指定为Foo
类型的变量都必须包含一个 key 为a
,value 为string
类型的字段。否则,TS 类型检查器就会报错。可以用
Record
来处理另外一种场景。假如我本来已经有了两个类型:我想把
Foo
和Bar
两个类型的 key 合并到一起,并给它们重新指定成 number 类型,可以使用Record
这样实现:此时,
Baz
的类型就相当于下面的类型定义了:Partial
Partial
在 TS 中的源码实现:它用来将 T 中的所有的属性都变成可选的。下面的示例中定义了一个类型
IFoo
,它拥有两个必选的属性 a 和 b。在将变量
foo
指定为IFoo
类型之后,它就必须同时包含 a 和 b 两个属性,否则就会报下面的类型检查错误:所以,如果我们能把
IFoo
的两个属性都变成可选的不就没问题了:Required
Required
在 TS 中的源码实现:它的作用正好和上面的
Partial
相反,是将 T 中的所有属性都变成必选的状态。下面示例中定义的类型IFoo
包含了两个可选的属性 a 和 b。所以,将变量foo
指定为IFoo
类型并且只包含一个 a 属性是不会有问题的。这时,我们可以用
Required
将IFoo
的所有属性都变成必选状态,否则,就报类型检查错误:Readonly
Readonly
在 TS 中的源码实现:这个从字面意思就可以理解是将一个类型的所有成员变为只读的状态。看下面的示例,肯定可以随意给
name
赋值成别的字符串值。用
Readonly
转换一下:Pick
Pick
在 TS 中的源码实现:它的作用是从 T 中将所有的 K 取出来,并生成一个新的类型。下面示例中定义的
IFoo
类型包含了两个必选属性 a 和 b。所以,将foo
指定为IFoo
类型之后,就肯定必须包含这两个属性,否则就会报类型检查错误:但是,如果我想让
foo
只包含IFoo
类型的 a 属性,就可以用Pick
这样来实现。它就是告诉 TS 仅仅将 a 属性从IFoo
中提取出来即可。注意,它和上面的
Partial
不一样的地方在于,Partial
是将类型中的所有的属性都变成了可选状态,而不能将某一个属性单独提取出来。Exclude
Exclude
在 TS 源码中的实现:它的作用是从 T 中排除掉所有包含的 U 属性。如果不明白这句话,就看下面示例。
代码运行之后,
TFoo
只会包含一个 2。这是因为Exclude
会从第一个类型参数中将其所有包含的第二个类型参数中的值给排除掉。我们可以看到在第一个类型参数中只包含第二个类型参数中的 1,因此,它就会被排除掉,只剩下 2 了。所以,如果一个变量被指定为了
TFoo
类型,它就只能被赋值为 2 了,否则就会报类型检查错误:Extract
Extract
在 TS 中的源码实现:它的作用正好和上面的
Exclude
相反。而是从 T 中提取出所有包含的 U 属性值。还是看下面的示例:TFoo
类型最终只会包含 1。这是因为 T 包含 U 中的属性值 1,Extract
会将它提取出来生成一个类型,也就相当于:NonNullable
NonNullable
在 TS 中的源码实现:它的作用是去除 T 中包含的
null
或者undefined
。如下示例所示,变量foo
的类型为TFoo
,因此下面的赋值都是没问题的。如果我想把
TFoo
中的null
和undefined
去除掉,可以这样处理:Parameters
Parameters
的 TS 源码实现:它的作用是用来获取一个函数的参数类型,而且返回的是只能包含一组类型的数组。
通过上面的示例可以看到通过
Parameters
获取到了Func
的参数类型,并返回的是数组形式:[string]
。因此,变量p
的赋值就只能是包含一个字符串类型值的数组。ConstructorParameters
ConstructorParameters
的 TS 源码实现:它的作用是用来获取一个类的构造函数参数类型,并以数组的形式返回。如果不明白这句话的意思,看下面的示例。类
Foo
的构造函数有两个参数,第一个为 string 类型,第二个为 number 类型。在使用
ConstructorParameters
处理之后,获取到的是一个类型数组。而且第一个值必须为 string 类型,第二个值必须为 number 类型。ReturnType
ReturnType
的 TS 源码实现:它用来得到一个函数的返回值类型。看下面的示例用
ReturnType
获取到Func
的返回值类型为string
,所以,foo
也就只能被赋值为字符串了。InstanceType
InstanceType
的 TS 源码实现:它的作用是获取一个类的实例类型,可以用获取到的实例类型来约束一个变量的赋值必须和类的成员类型完全一样才可以。看下面示例定义的类
Foo
中有一个字符串类型的x
,一个数字类型的y
,一个参数为字符串类型的方法say
我们用
InstanceType
获取类Foo
的实例类型,用来它约束变量foo
。那么,接下来给foo
赋值时就必须完全符合Foo
的成员类型才可以。假设你将变量
foo
中的x
值赋值为数字 1,就肯定会收到类型检查错误了:Omit
Omit
用来忽略 T 中的 K 属性Uppercase<StringType>
Uppercase
用来将每一个字符转换成大写形式Lowercase<StringType>
Lowercase
用来将每一个字符转换成小写形式Capitalize<StringType>
Capitalize
用来将第一个字符转换成大写形式ShoutyGreeting
的类型就变成了HELLO WORLD
非内置类型定义
下面这些并非是 TS 内置的类型定义,但是我觉得很实用的。所以,也想分享一下
DeepReadonly
DeepReadonly
用来深度遍历 T,并将其所有属性变成只读类型ConvertNumberToString
ConvertNumberToString
用来将number
转换为string
类型ValueOf
ValueOf
与keyof
相对应。取出指定类型的所有 valueMutable
用来将所有属性的
readonly
移除:ThisParameterType
用来提取一个函数中的
this
参数的类型,如果没有this
参数,则返回unknown
:OmitThisParameter
用来移除指定类型中的
this
参数this
,则直接返回该类型this
,则会将this
移除,再返回该类型ThisType
此类型不会返回任何转换之后的新类型。它只是用来对当前
this
上下文进行类型标注,在使用时必须开启[]()noImplicitThis
NestedKeyOf
此类型的作用是将如下类型:
转换为:
相关文章