四、TS 内置的工具类型

176 阅读5分钟

前言

本篇文章介绍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 泛型,排除泛型中的nullundefined

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代码更加优雅。