1、提取函数类型的参数:Parameters
Parameters 用于提取函数类型的参数类型。 源码如下
type Parameters<T extends (...args: any) => any>
= T extends (...args: infer P) => any
? P
: never;
//类型参数 T 为待处理的类型,通过 extends 约束为函数,参数和返回值任意
//通过 extends 匹配一个模式类型,提取参数的类型到 infer 声明的局部变量 P 中返回
例子
type test38=Parameters<(name:string,hobby:string[])=>{}>;
//type test38 = [name: string, hobby: string[]]
2、提取函数类型的返回值类型:ReturnType
ReturnType 用于提取函数类型的返回值类型。 源码:
type ReturnType<T extends (...args: any) => any>
= T extends (...args: any) => infer R
? R
: any;
例子
type test39 = ReturnType<(name: string, hobby: string[]) => 'hello'>;
//type test39 = "hello"
3、提取构造器参数的类型:ConstructorParameters
构造器类型和函数类型的区别就是可以被 new
Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型
源码:
type ConstructorParameters40<
T extends abstract new (...args: any) => any
> = T extends abstract new (...args: infer P) => any
? P
: never;
//通过 extends 约束为构造器类型,加个 abstract 代表不能直接被实例化(其实不加也行)
//用 T 匹配一个模式类型,提取参数的部分到 infer 声明的局部变量 P 里,返回 P
例子:
interface CatConstructor {
new(color: string): Cat
}
type test40=ConstructorParameters<CatConstructor>;
//type test40 = [color: string]
4、提取构造器返回值的类型:InstanceType
提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType
源码
type InstanceType<
T extends abstract new (...args: any) => any
> = T extends abstract new (...args: any) => infer R
? R
: any;
//整体和 ConstructorParameters 差不多,只不过提取的不再是参数了,而是返回值
例子
type Cat41 = {
catname: string;
}
interface CatConstructor41 {
new(color: string): Cat41
}
type test41 = InstanceType<CatConstructor41>
/*
type test41 = {
catname: string;
}
*/
5、函数可以调用 this,这个 this 的类型也可以约束:ThisParameterType
type Cat42 = {
color: 'black'
}
//函数里可以调用 this,这个 this 的类型也可以约束:
function CatMemo(this: Cat42) {
//console.log(this.color);
}
CatMemo.call({ color: 'black' });
源码:
type ThisParameterType42<T> =
T extends (this: infer U, ...args: any[]) => any
? U
: unknown;
//类型参数 T 为待处理的类型。
//用 T 匹配一个模式类型,提取 this 的类型到 infer 声明的局部变量 U 里返回。
//这样就实现了 this 类型的提取
6、删除 this 的类型:OmitThisParameter
源码
type OmitThisParameter43<T> =
unknown extends ThisParameterType<T>
? T
: T extends (...args: infer A) => infer R
? (...args: A) => R
: T;
//用 ThisParameterType 提取 T 的 this 类型,如果提取出来的类型是 unknown 或者 any,那么 unknown extends ThisParameterType 就成立,也就是没有指定 this 的类型,所以直接返回 T。
//否则,就通过模式匹配提取参数和返回值的类型到 infer 声明的局部变量 A 和 R 中,用它们构造新的函数类型返回。
例子
type Cat43 = {
color: 'black'
}
//函数里可以调用 this,这个 this 的类型也可以约束:
function CatMemo43(this: Cat42, age: number) {
//console.log(this.color);
}
type test43 = OmitThisParameter<typeof CatMemo43>;
//type test43 = (age: number) => void
7、把索引变为可选:Partial
源码:
type Partial44<T> = {
[P in keyof T]?: T[P];
};
例子:
type testPartial44=Partial<{race:'cat',age:1}>
/*
type testPartial44 = {
race?: "cat" | undefined;
age?: 1 | undefined;
}
*/
8、去掉可选:Required
源码
type Required45<T> = {
[P in keyof T]-?: T[P];
};
例子:
type testRequired45 = Required<{ race?: 'cat', age?: 1 }>
/*
type testRequired45 = {
race: 'cat';
age: 1;
}
*/
9、添加readonly
源码
type Readonly46<T> = {
readonly [P in keyof T]: T[P];
};
例子
type testReadonly46 = Readonly<{ race: 'cat', age: 1 }>
/*
type testReadonly46 = {
readonly race: 'cat';
readonly age: 1;
}
*/
10、Pick过滤构造新的索引类型
源码
type Pick47<T, K extends keyof T> = {
[P in K]: T[P];
};
例子
type testPick47 = Pick<{ race: 'cat', age: 1, hobby: ['play', 'eat'] }, 'race' | 'hobby'>;
/*
type testPick47 = {
race: 'cat';
hobby: ['play', 'eat'];
}
*/
11、去掉这部分索引构造成新的索引类型Omit
我们知道了 Pick 可以取出索引类型的一部分索引构造成新的索引类型,那反过来就是去掉这部分索引构造成新的索引类型。
可以结合 Exclude(下面13点) 来轻松实现:
源码
type Omit51<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
//类型参数 T 为待处理的类型
//类型参数 K 为索引允许的类型(string | number | symbol 或者 string)
//通过 Pick 取出一部分索引构造成新的索引类型
//这里用 Exclude 把 K 对应的索引去掉,把剩下的索引保留。
例子
type testOmit51=Omit<{ race: 'cat', age: 1, hobby: ['play', 'eat'] }, 'race' | 'hobby'>
/*
type testOmit51 = {
age: 1;
}
*/
12、创建索引类型:Record
源码
type Record48<K extends keyof any, T> = {
[P in K]: T;
};
//keyof any 是动态获取的,比直接写死 string | number | symbol 更好
//它用映射类型的语法创建了新的索引类型,索引来自 K,也就是 P in K,值是传入的 T
例子
type testRecord1 = Record<'cat' | 'dog', string>;
/*
type testRecord1 = {
cat: string;
dog: string;
}
*/
当传入的 K 是 string | number | symbol,那么创建的就是有可索引签名的索引类型:
type testRecord2 = Record<string, number>; \
/*
type testRecord2 = {
[x: string]: number;
}
*/
13、从联合类型中去掉一部分类型:Exclude
源码
type Exclude49<T, U> = T extends U ? never : T;
//联合类型当作为类型参数出现在条件类型左边时,会被分散成单个类型传入,这叫做分布式条件类型。
//所以写法上可以简化, T extends U 就是对每个类型的判断。
例子
type testExclude49=Exclude49<'a'|'b'|'c'|'d','a'|'b'>;
//type testExclude49 = "c" | "d"
14、从联合类型中保留一部分类型:Extract
源码
type Extract50<T, U> = T extends U ? T : never;
//可以过滤掉,自然也可以保留,Exclude 反过来就是 Extract,也就是取交集:
例子
type testExclude50 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'b'>;
//type testExclude50 = "a" | "b"
15、取Promise的ValuType的高级类型Awaited
在递归那节写过取 Promise 的 ValuType 的高级类型,这个比较常用,ts 也给内置了,就是 Awaited
源码
type Awaited52<T> =
T extends null | undefined
? T
: T extends object & { then(onfulfilled: infer F): any }
? F extends ((value: infer V, ...args: any) => any)
? Awaited<V>
: never
: T;
/*
类型参数 T 是待处理的类型。
如果 T 是 null 或者 undefined,就返回 T。
如果 T 是对象并且有 then 方法,那就提取 then 的参数,也就是 onfulfilled 函数的类型到 infer 声明的局部变量 F。
继续提取 onfullfilled 函数类型的第一个参数的类型,也就是 Promise 返回的值的类型到 infer 声明的局部变量 V。
递归的处理提取出来的 V,直到不再满足上面的条件。
这样就实现了取出嵌套 Promise 的值的类型的目的:
*/
例子
type testAwaited52=Awaited<Promise<Promise<Promise<number>>>>
//type testAwaited52 = number
16、判断是否为非空类型:NonNullable
NonNullable 就是用于判断是否为非空类型,也就是不是 null 或者 undefined 的类型的,实现比较简单
源码
type NonNullable<T> = T extends null | undefined ? never : T;
例子
type test1NonNullable53 = NonNullable<null>
//ype test1NonNullable53 = never
type test2NonNullable53 = NonNullable<{ race: 'cat' }>
/*
type test2NonNullable53 = {
race: 'cat';
}
*/
17、大写、小写、首字母大写、去掉首字母大写:Uppercase、Lowercase、Capitalize、Uncapitalize
源码
type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;
这个 intrinsic 是固有的意思,就像 js 里面的有的方法打印会显示 [native code] 一样。这部分类型不是在 ts 里实现的,而是编译过程中由 js 实现的。
其实就是 ts 编译器处理到这几个类型时就直接用 js 给算出来了。
例子:
//大写
type testUppercase54 = Uppercase<'abcd'>
//type testUppercase54 = "ABCD"
//小写
type testLowercase54 = Lowercase<'ABCD'>
//type testLowercase54 = "abcd"
//首字母大写
type testCapitalize54 = Capitalize<'abcd'>
//type testCapitalize54 = "Abcd"
//去掉首字母大写
type testUncapitalize54 = Uncapitalize<'Abcd'>
//type testUncapitalize54 = "abcd"
总结
比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。
用模式匹配 + 重新构造可以实现:OmitThisParameter
用重新构造可以实现:Partial、Required、Readonly、Pick、Record
用模式匹配 + 递归可以实现: Awaited
用联合类型在分布式条件类型的特性可以实现: Exclude
此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。