背景
距离上次强行硬核讲解infer已经有段时间了,不知道XDM能不能被我忽悠成功看懂infer,如果没看懂也没关系,在强行看一遍硬核讲解infer就可以了(狗头,哈哈)。然后进入我们今天的话题,相对来说比较轻松,开始我们Parameters和ReturnType的学习吧!
Parameters和ReturnType
Parameters官方例子
先来看下官方的例子,以及说明
Constructs a tuple type from the types used in the parameters of a function type Type.
declare function f1(arg: { a: number; b: string }): void;
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [s: string]
type T2 = Parameters<<T>(arg: T) => T>; // [arg: unknown]
type T3 = Parameters<typeof f1>; // [arg: {a: number, b: string}]
type T4 = Parameters<any>; // unknown[]
type T5 = Parameters<never>; // never
type T6 = Parameters<string>; // never
type T7 = Parameters<Function>; // never
先来翻译下Parameters干嘛用的,其实就是拿到一个函数的参数类型,注意返回的是tuple类型,根据官方的例子,我们来分析下答案
- T0 - 因为函数参数为空,所以答案是
[] - T1 - 因为函数参数只有一个string类型,所以答案是
[s: string] - T2 - 这边使用了泛型,又只有一个参数,所以答案是
[arg: unknown] - T3 - 这个应该是
Parameters用的是最多的例子,获取了f1函数的参数,参数只有1个对象,所以答案是[arg: {a: number, b: string}] - T4 - 你是any,反正我肯定是
tuple,所以答案是unknown[] - T5&T6&T7 - 这几个答案都是never,看了实现你就知道了
这边我来举个我初次使用Parameters的例子
function testParametersA (a: number, b: string) {
console.log({a, b})
}
在这个例子中,我们有个testParametersA的函数,他接受2个参数,一个number类型,一个string类型,之后我们有个需求需要实现testParametersB,好巧不巧,他的函数参数和testParametersA其实是一样的,如果low一点的实现,我继续照抄一遍testParametersA,那如果用Parameters该怎么实现呢,我们一步一步来
function testParametersB (args: Parameters<typeof testParametersA>) {
}
这里的args是不是就是tuple,且应该是[a: number, b: string],我们解构下,可以变成
function testParametersB ([a, b]: Parameters<typeof testParametersA>) {
}
但实现上又感觉不太对啊,我们明明希望传2个参数,那现在就变成传一个数组了,所以最终在加上三个点就可以了
function testParametersB (...[a, b]: Parameters<typeof testParametersA>) {
}
所以成功把函数A的参数类型,搬运到b了,完美!
源码讲解
直接上源码
/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
...args,实际已经让args变成了tuple类型,infer P推导泛型(是什么tuple),注意,这里的infer P写在的是参数的位置。我们接着看源码:如果继承了函数,则返回推导的tuple,否则返回never。PS:never因为是任何类型的子类型,所以官方例子中的type T5 = Parameters<never>; // never, 走的是true的分支
ReturnType官方例子
先来看下官方的例子,以及说明
Constructs a type consisting of the return type of function Type.
declare function f1(): { a: number; b: string };
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // unknown
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<typeof f1>; // {a: number, b: string}
type T5 = ReturnType<any>; // any
type T6 = ReturnType<never>; // never
type T7 = ReturnType<string>; // any
type T8 = ReturnType<Function>; //any
前面Parameters拿的是函数参数的类型,这次ReturnType拿的就是函数的返回值类型了,根据官方的例子,我们再来分析下答案
- T0 - 箭头函数都告诉你返回值是
string了 - T1 - 箭头函数都告诉你返回值是
void了 - T2 - 箭头函数都告诉你返回值是T,这个是泛型,那只能先
unknown了 - T3 - 花里胡哨,返回的是T类型,是个泛型,但T继承了U,U又继承了number数组类型,那我推导的结果就是
number[] - T4 - 箭头函数都告诉你返回值是
{a: number, b: string}了 - T5 - any可以继承函数,所以返回了any
- T6 - never可以继承函数,所以返回了never
- T7&T8 - 这几个答案都是any,看了实现你就知道了
这边我来举个我初次使用ReturnType的例子,大家能猜到是什么嘛,我第一次接触这个是用到定时器的时候,众所周知,定时器返回值应该是个数字的索引,我们的setTimeout不就是函数嘛,正好可以用一下
type timer = ReturnType<typeof setTimeout> // number
这个返回的就是number,但是不是比我们直接写number高端大气上档次点!
源码讲解
直接上源码
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
这次我们关注的是返回值,所以能看到我们的infer R是写在返回值的位置的,同样的继承逻辑,如果继承为true,推导出返回值的类型,否则返回any