TypeScript学习系列-infer关键字

70 阅读4分钟

一、前言

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关键字紧密相关的两个 原生内置类型,也就是ReturnTypeParameters的源码实现:

1. ReturnType

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
  1. type ReturnType<T extends (...args: any) => any>:这部分定义了ReturnType是一个泛型类型,它的参数T必须是一个函数类型。这个函数可以接收任意类型的参数(...args: any),并且返回任意类型的结果(=> any)。这里的any表示任意类型,因为我们不关心函数参数和返回值的具体类型,只关心返回值的类型。

  2. 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关键字,还深入探讨了它的使用方法、在项目中的应用场景以及ReturnTypeParameters源码实现。infer关键字就像是TypeScript世界中的瑞士军刀,虽然小巧,但功能强大。掌握它,你就能在TypeScript的世界里游刃有余。下次当你在代码中遇到类型推断的难题时,不妨拿出这把“刀”,看看它能否帮你解决问题。