Typescript中的工具类型
前言
写写那些Typescript
自带的工具类型吧,可能原理方面偏少,更多的是翻译
正文
Typescript
自带的工具类型,有些在源码中也经常的使用到,比如Record
在Vue@next
中就有用到
为了在IDE
中使用Typescript
,本文使用全局安装方式(yarn global add typescript
,npm
为npm install -g typescript
)
然后在IDEA
的Typescript
配置安装路径
IDEA 2020.2
默认有内置的Typescript
,版本为3.9.5
,不过现在是4.0.5
了
Partial<T>
Constructs a type with all properties of 'T' set to optional.
This utility will return a type that represents all subsets of a given type.
构造一个传入类型T
的所有属性都为可选的类型
简单地讲就是使得泛型T
的所有属性变为可选的
1 | interface User { |
可以看下它的源码
1 | /** |
使用keyof
来获取全部的属性名,然后使用in
来遍历,使用?
使得属性变为可配置,右侧的值为T[P]
对应每个属性对应的类型
Readonly<T>
Constructs a type with all properties of 'T' set to readonly,
meaning the properties of the constructed type cannot be reassigned.
构造一个传入类型T的所有属性都为只读的类型,这意味着构造出来的类型的属性无法被重新赋值
1 | interface User { |
注意这里的只读只对这个对象的属性生效,嵌套的属性不生效
1 | interface User { |
源码如下
1 | /** |
依然使用keyof
来获取全部的属性名,然后使用in
来遍历,右侧值为对应的类型
然后对每个属性前都用readonly
来标注,使之成为只读的
如果想让一个数组变为只读,可以使用Readonly<Array<T>>
,比如
1 | const arr: Readonly<Array<any>> = []; |
其实Typescript
已经内置了只读的数组类型ReadonlyArray<T>
1 | const arr: ReadonlyArray<number> = []; |
对于ReadonlyArray
和Array
的区别就是
ReadonlyArray
在整数属性和length
上使用了readonly
来修饰,而Array
没有
并且ReadonlyArray
少了那些会使得数组发送改变的方法,比如push
,pop
,shift
,unshift
,如下(省略了注释以及相同的API
)
1 | interface Array<T> { |
sort
和reverse
由于会交换数组中元素的位置(交换步骤会涉及到赋值),所以没有在ReadonlyArray
中
Record<K, T>
Constructs a type with a set of properties 'K' of type 'T'.
This utility can be used to map the properties of a type to another type.
构造一个属性为K
的集合,属性对应的类型为T
的类型
这个工具类型可以把一个类型的属性映射到另一个类型
1 | let record: Record<string, Function>; |
源码如下
1 | type Record<K extends keyof any, T> = { |
这里可能会疑惑keyof any
是个什么东西,可以试着给某个变量赋予看一下
发现变量o
类型为number
或者symbol
或者number
也就是泛型K
必须是这三个类型中的其中一种,如果使用Function
作为范型K
,是无效的
1 | let record: Record<Function, string>; // 报错 |
一般都是使用Record<string, T>
来生成一个类型为T
的映射对象
Pick<T, K>
Constructs a type by picking the set of properties 'K' from 'T'.
构造一个从T
中选取K
属性集的类型
1 | interface User { |
源码如下
1 | type Pick<T, K extends keyof T> = { |
keyof T
获取T
的属性集,依然使用in
来遍历属性集,每个属性对应类型为T[P]
这里可以看出K
必须是T
属性集的一个子集,不是的话会报错
1 | interface User { |
Omit<T, K>
Constructs a type by picking all properties from 'T' and then removing 'K'.
构造一个从T
中移除K
属性集的类型
1 | interface User { |
源码如下
1 | type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; |
可以看到,实现依赖了Pick
和Exclude
,可以先在左边点击Exclude
查看
Exclude<keyof T, K>
提取T
中不包含K
的部分,这时得到的只是属性名的集合类型,而不是属性名对应属性值类型
再使用Pick<T, Exclude<keyof T, K>>
把这部分提取出来,这时就变成属性名对应属性值类型
Exclude<T, E>
Constructs a type by excluding from 'T' all union members that are assignable to 'E'.
构造一个从T
中排除所有可以分配给E
的联合成员的类型
1 | type t1 = Exclude<"a" | "b" | "c", "c">; // "a" | "b" |
类型t1
应该很好理解,有趣的应该是t2
和t3
t2
由于number
无法分给1
,以及string
无法分给空字符串,所以还是string | number
而t3
和t2
相反,1
可以分给number
,以及空字符串可以分给string
,所以t3
类型为false
Extract<T, U>
Constructs a type by extracting from 'T' all union members that are assignable to 'U'.
构造一个从T
中提取所有可以分配给E
的联合成员的类型
这个和Exclude<T, E>
相反,大白话就是求交集,但是有单向的关系
1 | type t1 = Extract<"a" | "b" | "c", "c">; // "c" |
t1
还是很好理解,重点还是t2
和t3
t2
由于string无法分给空字符串,以及number
无法分给1
,导致"并集"为空,所以为never
t3
由于1
可以分给number
,以及空字符串可以分给string
,所以并集就为1 | ""
源码如下
1 | type Extract<T, U> = T extends U ? T : never; |
NonNullable<T>
Constructs a type by excluding null and undefined from 'T'.
构造一个从T
中排除null
和undefined
的类型
1 | type t = NonNullable<number | string | null | undefined>; // number | string |
源码如下
1 | type NonNullable<T> = T extends null | undefined ? never : T; |
Parameters<T>
Constructs a tuple type from the types used in the parameters of a function type 'T'.
构造一个类型T
函数的参数类型的元素类型
通俗点讲就是提取函数的参数,用数组装起来
1 | type f1 = (id: number, name: string) => void; |
源码如下
1 | type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; |
ConstructorParameters<T>
Constructs a tuple or array type from the types of a constructor function type.
It produces a tuple type with all the parameter types (or the type never if Type is not a function).
构造一个从构造函数的类型的元组或者数组类型,产生了一个所有参数类型的元组类型(或者当T
不是一个函数时为never
类型)
简单点讲就是提取构造函数的参数类型
1 | interface User { |
源码如下
1 | type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never; |
这里和Parameters
的区别就是ConstructorParameters
必须为构造函数,而Parameters
必须为普通函数,两着无法相互转换
ReturnType<T>
Constructs a type consisting of the return type of function 'T'.
构造一个由T
函数类型的返回类型组成的类型
简单点讲就是提取函数返回的类型
1 | interface User { |
源码如下
1 | type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; |
InstanceType<T>
Constructs a type consisting of the instance type of a constructor function in 'T'.
构造一个T
构造函数的实例类型的类型
简单点讲就是返回某个构造函数产生的实例的类型
1 | interface User { |
源码如下
1 | type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any; |
Required<T>
Constructs a type consisting of all properties of 'T' set to required.
The opposite ofPartial
.
构造一个T类型的所有属性集都是必须的类型
是Partial
操作的相反操作
1 | interface User { |
源码如下
1 | type Required<T> = { |
ThisParameterType<T>
Extracts the type of the this parameter for a function type,
or unknown if the function type has no this parameter.
提取一个函数类型的this
参数类型, 如果函数类型没有this
参数,那么为unknown
1 | type Context = { |
源码如下
1 | type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown; |
OmitThisParameter<T>
Removes the this parameter from
T
. IfT
has no explicitly declared this parameter, the result is simplyT
.
Otherwise, a new function type with no this parameter is created fromT
.
Generics are erased and only the last overload signature is propagated into the new function type.
删除T
的this
参数,如果T
没有明确的定义this
参数,结果为简单的T
否则,从T
生成一个没有this
参数的新的函数类型
泛型会被擦除,只有最后的重载签名会被传递到新的函数类型中。
1 | type Context = { |
源码如下
1 | type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T; |
ThisType<T>
This utility does not return a transformed type. Instead, it serves as a marker for a contextual this type.
Note that the –noImplicitThis flag must be enabled to use this utility.
这个工具不会返回一个转换的类型,而是用于标记一个this
的上下文的类型
使用--noImplicitThis
标志才能够使用这个工具
这个有点混入的意思在里面,这里借用官方的例子,记得在tsconfig.json
中配置
1 | { |
例子
1 | type ObjectDescriptor<D, M> = { |
源码如下
1 | interface ThisType<T> { } |
后记
重装了下npm
和yarn
,明明把安装路径放D
盘了,还是装到C
盘…
看起来还是非常不错的工具类,不过实现还是有些看不懂,比如泛型的?:
加上extends
,得找个时间好好看看…
以及一些有趣的语法,比如infer
,readonly
,-?
等