前言
本篇文章介绍ts内置的常用的工具类型,主要包括# Record、Partial 、 Required 、 Readonly、 Pick 、 Exclude 、 Extract 、 Omit。
一、Record
1. 源码:
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
接受两个泛型K、T, K做了类型限制,必须是string 或者 number 或者 symbol 的子类型。然后遍历K,对应的类型是T,生成一个对象类型。
2. 使用
模拟实现Record:
type MyRecord<T extends string | number | symbol, P> = {
[k in T]: P;
};
type objType = MyRecord<string | number | symbol, any>;
const myObj: objType = {
a: '123',
b: 123,
[Symbol.for('123')]: 1,
};
二、Partial
1. 源码
type MyPartial<T> = {
[p in keyof T]?: T[p];
};
Partial 接受一个对象类型并复制,唯一不同的是 会把对象的每一个参数都转换为可选的。
2. 使用
模拟实现Partial
type MyPartial<T> = {
[p in keyof T]?: T[p];
};
type personType = {
name: string;
age: number;
salary: number;
};
type partialPersonType = MyPartial<personType>;
// 等价于
/*type partialPersonType = {
name?: string;
age?: number;
salary?: number;
};*/
三、Required
1. 源码
type myRequired<T> = {
[k in keyof T]-?:T[p];
}
Required接受一个对象类型并复制,唯一不同的是 会把对象的每一个参数都转换为必选的。
2. 使用
模拟实现Required
type MyRequired<T> = {
[p in keyof T]-?: T[p];
};
type personType = {
name?: string;
age?: number;
salary?: number;
};
type requiredPersonType = MyRequired<personType>;
// 等价于
/*type requiredPersonType = {
name: string;
age: number;
salary: number;
};*/
四、Exclude
1. 源码
type myExclude<T,P > = T extends P ? never : T;
Exclude接受一个两个泛型,第一个是联合类型(number | string ...),第二个是需要排除的类型的联合。 Exclude 会返回T中排除P以后剩下的类型。
2. 使用
模拟实现Exclude
type MyExclude<T, U> = T extends U ? never : T;
type a = number | string | boolean;
type b = MyExclude<a, number>;
// 等价于
// type b =string | boolean;
五、Pick
1. 源码
type myPick<T, K extends keyof T> = {
[p in K]: T[p];
};
Pick接受一个两个泛型,第一个是一个对象类型,第二个是必须是对象的键的联合类型的子类型,Pick会挑选出指定的key对应的类型, 然后组合成一个新的对象类型并返回。
2. 使用
模拟实现Pick
type myPick<T, K extends keyof T> = {
[p in K]: T[p];
};
type personType = {
name: string;
age: number;
salary: number;
};
type pickNameAndAge = myPick<personType, 'name' | 'age'>;
// 等价于
/*type pickNameAndAge = { name: string; age: number };*/
六、Omit
1. 源码
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
Omit 接受一个两个泛型,第一个是一个对象类型,第二个是需要去除的对象中的键,Pick会省略指定的key对应的类型, 然后组合成一个新的对象类型并返回。
2. 使用
模拟实现Omit
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type personType = {
name: string;
age: number;
salary: number;
};
type p = MyOmit<personType, 'name'>;
// 等价于
// type p = { age: number; salary: number };
六、Extract
1. 源码
type MyExtract<T, U> = T extends U ? T : never;
Extract 接受一个两个泛型,第一个是一个联合类型,第二个是需要选取的联合类型,Extract会选择 T 中,且在 U 中的类型组合成新的联合类型返回。
2. 使用
模拟实现 Extract
// 和Exclude 相反, 联合类型的挑选
type MyExtract<T, U> = T extends U ? T : never;
type a = number | string | boolean;
type b = MyExtract<a, number | boolean>;
// 等价于
// type b = number | boolean
七、ReturnType
1. 源码
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
ReturnType 接受一个函数签名,返回函数的的返回值的类型。
2. 使用
模拟实现 ReturnType
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
type funType = (a: number) => string;
type funReturnType = MyReturnType<funType>;
// 等价于
// type funReturnType = string
八、InstanceType
1. 源码
type MyInstanceType<T extends new (...args: any[]) => any> = T extends new (...arg: any[]) => infer R ? R : never;
InstanceType 接受一个构造函数签名,返回构造函数的的返回值的类型。
2. 使用
模拟实现 InstanceType
// 获取构造函数的返回值
type MyInstanceType<T extends new (...args: any[]) => any> = T extends new (...arg: any[]) => infer R ? R : never;
type FuncType = new (...args: any[]) => { name: string; age: number };
type FuncTypeReturn = MyInstanceType<FuncType>;
再来看一个InstanceType 的应用:
class Person {
name:string = "Rock"
age:number = 18
}
// 如何获取class 类的 所有属性的联合类型
// type personPropertiesType = 'name' | 'age'
如何获取class 类的 所有属性的联合类型?
就像这样 :type personPropertiesType = 'name' | 'age'
其实很简单,Person 本质就是一个构造函数。我们先获取构造函数的返回值值(其实就是类的实例对象的类型),然后keyof 一下
type personPropertiesType = keyof InstanceType<typeof Person>
解释:typeof Person 可以提取构造函数的类型。InstanceType 接受一个构造函数的类型,返回实例对象的类型, 获取到实例对象的类型以后,再通过keyof 获取键的联合类型即可
九、Parameters
1. 源码
type MyParameters<T extends new (...args: any[]) => any> = T extends new (...arg: any[]) => infer R ? R : never;
InstanceType 接收一个构造函数签名,以元组的形式获取函数的参数类型
2. 使用
模拟实现 Parameters
// 以元组的形式获取函数的参数类型
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never;
type funType = (a: number) => string;
type funParameters = MyParameters<funType>;
// 等价于
// type funReturnType = [number]
十、ConstructParameters
1. 源码
type MyConstructParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer R) => any ? R : never;
ConstructParameters 接受一个构造函数签名,返回构造函数的的返回值的类型。
2. 使用
模拟实现 ConstructParameters
// 获取构造函数的返回值
// 以元组的形式获取构造函数的参数类型
type MyConstructParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer R) => any ? R : never;
type FuncType = new (name: string, age: number) => any;
type ConstructParametersType = MyConstructParameters<FuncType>;
// 等价于
// type ConstructParametersType = [string, number];
十一、NonNullable
1. 源码
type MyNonNullable<T> = T extends null | undefined ? never : T;
NonNullable 泛型,排除泛型中的null 和 undefined
2. 使用
模拟实现 NonNullable
// 排除类型中的undefined 和 null
type MyNonNullable<T> = T extends null | undefined ? never : T;
type a = string | number | undefined | null;
type b = MyNonNullable<a>;
// 等价于 type b = string | number
总结
熟悉常用的内置类型工具,可以让我们在日常开发过程中对类型的运行更加高效,合理使用类型计算函数,可以让我们的ts代码更加优雅。