前言
本文收录于TypeScript知识总结系列文章,欢迎指正!
前面的文章我们对泛型的用法有了大概的认识,也通过泛型实现了一些常用的类型,那么这篇文章将带各位深入了解一些常用泛型工具类型的使用及实现,那么话不多说,我们直接开始。
Partial<T>
Partial<T>的作用是将类型T中的所有属性变为可选属性
interface IAnimal { name: string age: number color: string } type MyPartial = Partial<IAnimal>
相当于转换成了
type MyPartial = { name?: string; age?: number; color?: string; }
它的实现是使用映射类型对对象属性遍历
type Partial<T> = { [P in keyof T]?: T[P]; };
Required<T>
Required与Partial相反,作用是将所有属性变成必选属性,用到的原理就是 '-?' 符号
type MyRequired = Required<MyPartial>
上面的代码中的MyRequired变回了IAnimal,以下是Required的实现
type Required<T> = { [P in keyof T]-?: T[P]; };
Readonly<T>
只读属性我们在前面的文章也提及过,将所有的属性换成只读
type MyReadonly = Readonly<MyRequired>
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
Pick<T, K>
Pick的作用是在对象中选取一部分属性,比如我们选取name和age
type MyPick = Pick<IAnimal, "name" | "age">
他的实现过程是传入两个类型参数,第一个是对象类型,第二个是属性集合
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
Exclude<T, U>
Exclude<T, U>表示从T中排除U类型或类型集合,如下面代码排除了string类型
type MyExclude = Exclude<string | number | boolean, string>
此外,我们可以将Exclude融入对象类型,针对属性值进行排除,如
type ExcludeAnimal = { [key in Exclude<keyof IAnimal, string>]: IAnimal[key] }
上述代码实现的是将IAnimal中字符类型属性名全部排除,也就是将属性全清除
Exclude的实现
type Exclude<T, U> = T extends U ? never : T;
Omit<T, K>
Omit<T, K>的含义是从T对象类型中删除K,比如我们可以删除IAnimal的age和color
type OmitAnimal = Omit<IAnimal, "age" | "color">
它的实现方式也是通过Pick+Exclude实现的,仅需记住两步:Exclude排除T中K的类型,返回值传给Pick;使用Pick生成新的对象类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Record<K, T>
Record可以理解为批量创建一个对象中的属性,其中键值或其集合是K,类型是T。我们使用Record表示一下上面的IAnimal
type RecordAnimal = Record<"name" | "color", string> & Record<"age", number>
实现方式如下,其中P是K的子集代表着对象的属性
type Record<K extends keyof any, T> = { [P in K]: T; };
上面的代码实际上我们可以这么写,通过string | number | symbol集合保证key的安全
type IRecord<K extends string | number | symbol, T> = { [key in K]: T }
NonNullable<T>
NonNullable<T>一般用来从类型T中排除空类型,即判断是否存在null或undefined,如果不是则返回原类型
NonNullable<null | undefined | string | boolean | number> // 表示 string | number | boolean
类型实现
type NonNullable<T> = T extends null | undefined ? never : T;
ReturnType<T>
ReturnType是用来获取函数的返回值类型,其中T表示函数类型
type foo<T> = (params: T) => T[] type IReturnType = ReturnType<foo<string>>
当函数并未调用时,我们无法获得泛型的类型,此时使用ReturnType则会获得unknown类型
function foo<T>(params: T) { return [params] } type IReturnType = ReturnType<typeof foo> // unknown[]
ReturnType的实现就需要使用到我们前面文章说到的infer关键字了
我们复习一下返回函数的返回值类型是怎么实现的?
type IReturn<T> = T extends (...args: any[]) => infer R ? R : T; type foo = () => string type IReturnType = IReturn<foo>// string
然后我们给泛型加个约束,泛型只能传入函数类型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
这样,一个获取函数返回值的类型就实现完成了
Parameters<T>
说完了获取函数返回值类型,咱们看看Parameters<T>获取函数参数类型,通过传入函数T来获取T的参数类型
type foo<T> = (params: T) => T type IGetParameters = Parameters<foo<string>>// [params: string]
参数类型像arguments数组一样,思考以下代码,使用infer实现获取函数参数类型
type IParameters<T> = T extends (...args: infer P) => any ? P : T;
与函数返回值一样,通过添加一个函数的约束来实现完整功能
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
ConstructorParameters<T>
ConstructorParameters表示获取构造函数T的参数类型,我们使用接口定义一个类的构造函数,并通过ConstructorParameters获取其参数的伪数组
interface IAnimal { new(name: string, age: number) } type getConstructorParameters = ConstructorParameters<IAnimal> // [name: string, age: number]
实现方式与Parameters类似,只不过把函数类型换成了类
type IConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
tips:官方的对此类型的实现中有个巧妙的地方:使用抽象类实现了该类型。原因是什么?我们看看下面的代码
// 抽象类 abstract class Animal { } // 类 class Dog { } // 使用类类型实现获取构造函数参数类型 type ICParameters<T> = T extends new (...args: infer P) => any ? P : never; // 使用抽象类类型实现获取构造函数参数类型 type IAbstractCParameters<T> = T extends abstract new (...args: infer P) => any ? P : never; // 抽象类类型-抽象类 type getAbstractCParametersAnimal = IAbstractCParameters<typeof Animal>// [] // 抽象类类型-类 type getAbstractCParametersDog = IAbstractCParameters<typeof Dog>// [] // 类类型-抽象类 type getCParametersAnimal = ICParameters<typeof Animal>// never // 类类型-类 type getCParametersDog = ICParameters<typeof Dog>// []
可以看到由于抽象类只能被类继承,而不能反过来继承类,还记得之前讲述的关于类的文章吗?抽象类只能被继承,不能被实例化,并且只能被类或者抽象类继承。所以理解为使用抽象类实现获取构造函数参数类型可以运用于抽象类,而使用类实现获取构造函数参数类型无法运用于抽象类。描述的比较乱,仅供参考,具体看看代码会清晰一点
最后在上述的基础上给ConstructorParameters类型增加一个抽象类的约束就可以通过以下代码实现完整类型了
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
InstanceType<T>
InstanceType用于获取类的实例化类型,即 new 类 的产物,可以说是执行构造函数的返回值,与上面的ConstructorParameters类型相对应
class Animal { constructor(name?: string, age?: number) { } } const animal: InstanceType<typeof Animal> = new Animal()
实现方式也类似,就不多做描述
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
ThisParameterType<T>
ThisParameterType获取函数参数的this的类型,常常用在call,apply,bind中。
function getThis(this: any) { console.log(this); } type getThisType = ThisParameterType<typeof getThis>;// string
但同时,只要函数内部的第一个参数不命名为this,这个类型就表示unknown
function getThis(str: string) { console.log(str); } type getThisType = ThisParameterType<typeof getThis>;// unknown
tips:编译后的函数第一个参数会消失
function getThis() { console.log(this); }
实现方式是获取函数的第一个为this参数的类型
type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any ? U : unknown;
OmitThisParameter<T>
OmitThisParameter的用途是若函数的第一个参数为this,则将其删除并返回函数类型
function getThis(this: string) { console.log(this); } type getThisType = OmitThisParameter<typeof getThis>;// () => void
其实现方式如下
type OmitThisParameter<T> = T extends (this: any, ...args: infer P) => infer R ? (...args: P) => R : T;
写在最后
以上就是文章全部内容了,本篇文章针对泛型常用的工具类型做了详细的解读及实现,有什么问题还望多多指教,感谢你看到了最后,如果文章对你有帮助,还望支持一下,谢谢!