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. IfThas 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,-?等