一、前言
Hello~大家好,我是秋天的一阵风。
infer
是TypeScript 2.8版本引入的一个关键字,它用于条件类型中,允许我们从另一个类型中推断出一个新的类型变量。简单来说,infer
就像是一把钥匙,能够打开类型推断的大门。
二、使用方法
infer
的使用方法非常直接,你可以在条件类型中使用它来创建一个新的类型变量。例如:
type Foo<T> = T extends infer U ? U : never;
在这个例子中,infer U
会推断出T
的类型,并将这个类型赋值给U
。
三、上手案例
空谈误国,实干兴邦!说一千道一万,还不如直接Show me your code
。我们来几个小案例练练手:
1. 实现MyReturn
我们需要实现一个 MyReturn
来获取传入函数的返回值类型,如下代码所示:
type MyReturn<T> = ???;
type Foo = (a: string,b: string) => string;
type Bar = (a: any[],b: any[]) => any[];
let FooResult : MyReturn<Foo> // string
let BarResult : MyReturn<Bar> // any[]
那么应该如何实现呢?我们可以先拆解成这种形式:
type MyReturn<T> = T extends Function ? 返回类型 : T;
但是这样写问题就来了,我们没办法获得函数的返回类型,所以我们要将Function
改造成通用的写法,也就是下面这样:
type MyReturn<T> = T extends (...args: any[]) => 返回类型 ? 返回类型 : T;
接下来我们只需要在“返回类型”这里做文章就行了,我们可以借助typescript的推导改造成下面的代码:
type MyReturn<T> = T extends (...args: any[]) => infer R ? R : T;
typescript会将函数的返回类型推导出来,并且放入到R里面,所以我们直接返回R就行了。
2. 获取函数第一个参数的类型
同样的,我们先看题目:
type FirstArg<T> = ?? ;
type firstArg = FirstArg<(name:string,age:number)=>void>; // string
我们依葫芦画瓢,先拆解成下面这种形式:
type FirstArg<T> = T extends 函数 ? xxx: T ;
再改造成函数的通用写法:
type FirstArg<T> = T extends (arg: infer P, ...args: any[]) => any ? P : T;
这样我们就能将推导出来的P得到,并且返回。
3. 获取Promise类型
type PromiseType<T> = ???;
type pt = PromiseType<Promise<string>>; // string
这个就是经典例子了,话不多说,我们直接秒了:
type PromiseType<T> = T extends Promise<infer P> ? P : T;
四、ReturnType和Parameters的源码实现
我们学完了infer
的用法案例以后,我们来继续探究Typescirpt
中跟infer
关键字紧密相关的两个
原生内置类型,也就是ReturnType
和Parameters
的源码实现:
1. ReturnType
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
-
type ReturnType<T extends (...args: any) => any>
:这部分定义了ReturnType
是一个泛型类型,它的参数T
必须是一个函数类型。这个函数可以接收任意类型的参数(...args: any
),并且返回任意类型的结果(=> any
)。这里的any
表示任意类型,因为我们不关心函数参数和返回值的具体类型,只关心返回值的类型。 -
T extends (...args: any) => infer R ? R : any
:这部分是条件类型,它是TypeScript中一个强大的特性,允许我们根据类型T
的形状来推断出一个新类型。T extends (...args: any) => infer R
:这是一个条件检查,意思是如果T
是一个函数,并且这个函数返回一个值,那么我们就用infer R
来创建一个新的类型变量R
,这个R
就是函数返回值的类型。? R : any
:这部分是条件类型的结果。如果上面的条件成立,即T
确实是一个函数,那么我们就会使用R
,也就是函数返回值的类型。如果T
不是一个函数,那么ReturnType
就会默认为any
类型,表示任意类型。
2. Parameters
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
T extends (...args: any) => any
:这部分指定了T
必须是一个函数类型,可以接收任意类型的参数,并返回任意类型的结果。T extends (...args: infer P) => any ? P : never
:这是一个条件类型,如果T
是一个函数类型,那么P
将是一个元组类型,包含了函数所有参数的类型。如果不是函数类型,则返回never
类型。
五、总结
通过这篇文章,我们不仅了解了infer
关键字,还深入探讨了它的使用方法、在项目中的应用场景以及ReturnType
和Parameters
源码实现。infer
关键字就像是TypeScript
世界中的瑞士军刀,虽然小巧,但功能强大。掌握它,你就能在TypeScript
的世界里游刃有余。下次当你在代码中遇到类型推断的难题时,不妨拿出这把“刀”,看看它能否帮你解决问题。