当前位置: 首页 > 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/news/316858.html

相关文章:

  • 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
  • pSort
  • < 微积分Calculus >
  • 【自学笔记】支持向量机(3)——软间隔
  • MySQL--导入SQL文件(命令行导入)
  • 马尔科夫蒙特卡洛_吉布斯抽样算法(Markov Chain Monte Carlo(MCMC)_Gibbs Sampling)
  • 小程序服务零工市场
  • 基于51单片机的矿井安全检测系统
  • SpringBoot 整合 apache fileupload 轻松实现文件上传与下载(通用版)
  • LeetCode 260. 只出现一次的数字 III
  • 用Python提取PowerPoint演示文稿中的音频和视频