当前位置: 首页 > article >正文

Typescript进阶:解锁Infer类型的使用

alt

Infer特性是在 TypeScript 2.8 版本中引入的,由Ahejlsberg提交的PR,Ahejlsberg是微软技术研究员和TypeScript的首席架构师,C#、Delphi和Turbo Pascal的原创设计师。

https://github.com/Microsoft/TypeScript/pull/21496

infer是什么

infer 关键字用于在条件类型(conditional types)中声明一个类型变量,并从待推断的类型(type to be inferred)中推断出该类型。它的作用是从类型关系中提取出信息,用以满足条件类型的逻辑需求。

这里面有几个重点:

  1. 用于条件类型中:infer 只能用于 extends 条件类型中。
  2. 声明类型变量:infer 后跟一个类型变量,这个变量会用来接收被推断出来的类型。
  3. 从类型中推断:infer 的作用是从符合条件的类型中“提取”出具体的类型,类似于函数的参数解构。

简单来说,它就像是一种“类型占位符”,可以自动帮你在复杂的类型表达式中找到你想要的类型,然后把它提取出来。

例子解析

案例1:数组类型推导

type ArrayElement<T extends any[]> = T extends (infer Element)[] ? Element : never;

效果:

type NumArray = number[];
type StrArray = string[];
type BoolArray = boolean[];
type EmptyArray = never[];
type NotArray = 42;

type First = ArrayElement<NumArray>;  // number
type Second = ArrayElement<StrArray>; // string
type Third = ArrayElement<BoolArray>; // boolean
type Empty = ArrayElement<EmptyArray>;  // never (因为 never[] 的元素类型是 never)
type Not = ArrayElement<NotArray>;      // never (因为 NotArray 不是数组类型)

这个类型的拆分理解如下:

  1. T extends any[]:这部分指定了 T 是一个数组类型。any[] 表示数组,而 T extends any[] 表示 T 是扩展自数组的类型,即 T 是某种数组。

  2. T extends (infer Element)[]:这是条件类型的核心部分。这里,我们使用 infer 关键字来推断数组 T 的元素类型。(infer Element)[] 的意思是“如果 T 是一个数组,那么推断这个数组的元素类型为 Element”。

  3. ? Element:如果条件 T extends (infer Element)[] 为真,即 T 确实是一个数组,那么类型为 Element,也就是数组的元素类型。

  4. : never:如果条件为假,即 T 不是一个数组,那么类型为 nevernever 是一个表示“永不存在的类型”的类型,它通常用于表示错误的类型或不可能出现的情况。

案例2:函数返回类型

ReturnType 是一个TS内置的类型,其内部实现如下

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;

效果:

type Func1 = () => number;
type Func2 = (a: string, b: number) => boolean;
type Func3 = (x: any) => void;
type Func4 = (message: string) => Promise<string>;
type Func5 = <T>(item: T) => T[];

type Returned1 = ReturnType<Func1>;  // number
type Returned2 = ReturnType<Func2>;  // boolean
type Returned3 = ReturnType<Func3>;  // void
type Returned4 = ReturnType<Func4>;  // Promise<string>
type Returned5 = ReturnType<Func5>;  // T[]

这个类型的拆分理解如下:

  1. T extends (...args: any[]) => any:这部分指定了 T 是一个函数类型,这个函数可以接收任意数量的参数(...args: any[]),并且返回任意类型(any)。这里的 ...args 表示函数的参数是一个任意长度的参数列表。

  2. T extends (...args: any[]) => infer R:这是条件类型的核心部分。这里,我们使用 infer 关键字来推断函数 T 的返回类型。(...args: any[]) => infer R 的意思是“如果 T 是一个函数,那么推断这个函数的返回类型为 R”。

  3. ? R:如果条件 T extends (...args: any[]) => infer R 为真,即 T 确实是一个函数,那么类型为 R,也就是函数的返回类型。

  4. : any:如果条件为假,即 T 不是一个函数,那么类型为 any。这是一种保守的回退选项,但实际上,由于我们已经有了 T extends (...args: any[]) => any 的约束,这种情况是不会发生的。

与这个类似的,TS还有一个内置的通用的工具方法,从函数类型中提取参数类型,并将它们作为一个元组类型

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

例子:

type Func1 = (a: number, b: string) => void;
type Func2 = (x: boolean, y: number, z: string) => boolean;

type Params1 = Parameters<Func1>;  // [number, string]
type Params2 = Parameters<Func2>;  // [boolean, number, string]

其与获取返回值最大的不同在于T extends (...args: infer P) => any ? P : never;

  1. T extends (...args: infer P) => any:它的意思是“如果 T 是一个函数,那么推断这个函数的参数类型为 P”。
  2. ? P:如果条件 T extends (...args: infer P) => any 为真,即 T 确实是一个函数,那么类型为 P,也就是函数的参数类型列表。
  3. : never:如果条件为假,即 T 不是一个函数,那么类型为 never。这是一种保守的回退选项,但实际上,由于我们已经有了 T extends (...args: any) => any 的约束,这种情况是不会发生的。

如果没有infer

对于上面提到的例子2,如果没有Infer,要实现类似 ReturnType<T> 的功能,我们只能通过列举的方式来实现。

type GetReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => string ? string :
                                                        T extends (...args: any[]) => number ? number :
                                                        T extends (...args: any[]) => boolean ? boolean :
                                                        any;

// 测试
const test1: GetReturnType<() => number> = 123;  // 推断为 number
const test2: GetReturnType<() => string> = "Hello";  // 推断为 string
const test3: GetReturnType<() => boolean> = true;  // 推断为 boolean

这里通过多层条件类型手动匹配返回类型。所以infer还是很有帮助的,它能节省很多的判断类型。

总结

infer 就是 TypeScript 的“自动推理器”,它帮你在类型定义里自动找出某个特定的类型,而不需要你手动去指定。你只需要告诉 TypeScript 去哪个位置推断类型,它就会帮你填上正确的类型。其好处可以归纳为这几点:

  • 灵活性:infer 提供了一种灵活的方式来处理和构建类型,特别是在处理泛型时。
  • 代码复用:通过使用 infer,可以创建更通用的类型实用程序,这些实用程序可以在多个地方重用。
  • 类型安全:infer 可以帮助确保类型的正确性,减少类型错误的可能性。

另外需要记住infer仅能用于 extends 子句的推断,其他地方会报错。

本文由 mdnice 多平台发布


http://www.kler.cn/a/316858.html

相关文章:

  • MySQL的SQL书写顺序和执行顺序
  • 大数据新视界 -- 大数据大厂之 Impala 存储格式转换:从原理到实践,开启大数据性能优化星际之旅(下)(20/30)
  • 【mysql】使用宝塔面板在云服务器上安装MySQL数据库并实现远程连接
  • 论文解析:边缘计算网络中资源共享的分布式协议(2区)
  • INQUIRE:一个包含五百万张自然世界图像,涵盖10,000个不同物种的专为专家级文本到图像检索任务设计的新型基准数据集。
  • 限流算法(令牌通漏桶计数器)
  • C++笔记---set和map
  • NLP--自然语言处理学习-day1
  • 《微信小程序实战(3) · 推广海报制作》
  • 文件系统(软硬链接 动静态库 动态库加载的过程)
  • C++学习笔记(32)
  • 在C#中使用NPOI将表格中的数据导入excel中
  • 工业交换机如何保证数据的访问安全
  • SkyWalking 简介
  • 深入理解Go语言中的并发封闭与for-select循环模式
  • 使用脚本自动化管理外部Git仓库依赖
  • 如何基于Flink CDC与OceanBase构建实时数仓,实现简化链路,高效排查
  • MySQL面试题——第一篇
  • 人工智能不是人工“制”能
  • FreeSWITCH 简单图形化界面29 - 使用mod_xml_curl 动态获取配置、用户、网关数据
  • 寻呼机爆炸,炸醒通讯安全警惕心
  • 【操作系统强化】王道强化一轮笔记
  • k8s1.27.7部署higress,代理非k8s集群业务
  • 如何借助ChatGPT提升论文质量:实战指南
  • 真正能抵抗裁员的,从不是专业能力,早知道这些都财务自由了
  • JAVA_17