前言

TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。它是 JavaScript 的超集,不仅支持原生JS的写法,还增加了很多有用、强大的功能。众所周知, JavaScript 是弱类型语言,一些很低级却难以发现的 Bug 只有在运行时才能发现,而 TypeScript 却能够在编写代码的过程中就能避免绝大部分的低级报错。本专栏用于记录和分享我在使用 TypeScript 中的感悟和心得,以下来介绍一下 infer 的用法:

infer 关键字

在有条件类型的 extends 子语句中,允许出现 infer 声明,它会引入一个待推断的类型变量。 这个推断的类型变量可以在有条件类型的 true 分支中被引用。

这就是 infer 的解释,是不是上来一看就有点懵。晦涩的描述从字面意思来看确实很难懂,通过分解知识点以及结合一些例子后就很好理解了。首先看一下什么是条件类型:

条件类型

像这样 T extends U ? X : Y 的表达式就是条件类型,结合以下代码:

type ConditionType<T> = T extends Boolean ? number : undefinedconst a: boolean = false
const b: string = 'false'type TypeA = typeof a // boolean
type TypeB = typeof b // stringtype T1 = ConditionType<TypeA> // number
type T2 = ConditionType<TypeB> // undefined

ConditionType 是我定义的一个简单的工具类型,它可以接收一个 泛型参数 T,并分别返回不同的类型。以上第一行代码可以理解为:如果 T 是布尔类型则返回 number 类型,不是则返回 undefined 类型。甚至,我们还可以约束传入的 泛型参数 T 的类型,让我们的代码更严谨一点,如下:

type Types = boolean | string
type ConditionType<T extends Types> = T extends Boolean ? number : undefinedtype TypeC = object
type T3 = ConditionType<TypeC> // Error: Type 'object' does not satisfy the constraint 'Types'.

通过对泛型参数类型的约束,传入其他类型会报错。

ReturnType(获取函数返回值类型)

结合 Typescript 内置工具类型 ReturnType 来理解 “使用 infer 关键字来声明一个待推断的类型变量”。ReturnType 可以获取函数返回值类型,它的核心也是 infer 来实现的,代码如下:

/*** Obtain the return type of a function type*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

它的用法非常简单,如下:

const add = (x: number, y: number) => x + ytype Type1 = typeof add // (x: number, y: number) => number
type T1 = ReturnType<Type1> // number

T extends (...args: any) => any 这一行代码就是约束传入 ReturnType 的泛型参数类型必须是函数类型,这就类似于参数校验。这里的 T 就是 add 函数的类型 (x: number, y: number) => number

那么 T extends (...args: any) => infer R ? R : any 代码的含义可以理解为:如果 T (x: number, y: number) => number 满足于 (…args: any) => infer R 的表现形式的话,就返回 R,否则返回 any

infer R 表示声明一个待推断的类型,并将推断出来的类型存储在 R 中。这里 T 是 (x: number, y: number) => numberinfer R 被放在 (...args: any) => infer R 函数类型的返回值处,因此推断出了 number 类型并赋值给 R 。

infer 推断函数参数类型

ReturnType 借助 infer 实现获取函数的返回值类型,那么如何获取函数参数类型?虽然 TypeScript 已实现该工具函数,但不妨碍我们写一个。很简单,在 ReturnType 上改一下即可:

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

仅是将 infer R 跟参数类型 any 调换一下就实现了,看下打印:

type ParamsType<T extends (...args: any) => any> = T extends (...args: infer R) => any ? R : any;const add = (a: number, b: string) => a + btype TypeAdd = typeof add // (a: number, b: string) => stringtype T1 = ParamsType<TypeAdd> // [number, string]const arr: T1 = [1, 2] // Error: Type 'number' is not assignable to type 'string'.

由于上述采用的是 ES6 rest 参数写法,所以它会推断出一个元组类型,如果是想获取单个参数类型可以这样写:

type ParamType<T extends (arg: any) => any> = T extends (arg: infer R) => any ? R : any;

总结

以上便是我对于 infer 的粗浅理解,其实 infer 很强大能实现更多的功能。比如:将类构造函数类型的参数推断为元组的 ConstructorParameters 、获取构造函数类型的返回类型的 InstanceType ,还能通过 infer 推断出数组的子元素类型等等一系列骚操作……

并且 Vue3 中的 ref 为什么能够实现各种复杂的类型推断,也是通过 infer 来实现的,写的有点复杂,感兴趣的童鞋可以看一下 Vue3 ref 源码 。

参考

  • https://www.tslang.cn/docs/release-notes/typescript-2.8.html
  • https://www.wenjiangs.com/doc/typescript-infer

TypeScript——理解 infer 关键字!相关推荐

  1. 在条件类型中使用 infer 关键字

    在 TypeScript 中条件类型的用法是: T extends U ? X : Y 跟 JS 中的条件表达式一样,如果 extends 语句为真,则取X类型 ,反之得到Y类型 .我们这里把X称为条 ...

  2. synchronized()_这篇文章带你彻底理解synchronized关键字

    Synchronized关键字一直是工作和面试中的重点.这篇文章准备彻彻底底的从基础使用到原理缺陷等各个方面来一个分析,这篇文章由于篇幅比较长,但是如果你有时间和耐心,相信会有一个比较大的收获,所以, ...

  3. 对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相

    一:背景 1. 讲故事 昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为:[C#.NET 拾遗补漏]10:理解 volatile 关键字 (https://www.cnblogs.com/will ...

  4. 程序员你真的理解final关键字吗?

    文章目录 1.修饰类 2.修饰方法 3.修饰变量 4.final变量修饰变量(成员变量.局部变量) 4.1 final修饰成员变量: 4.2 final修饰局部变量: 5.final变量和普通变量的区 ...

  5. 深入理解static关键字

    文章目录 1.static存在的主要意义 2.static的独特之处 3.static应用场景 4.静态变量和实例变量的概念 5.静态变量和实例变量区别[重点常用] 6.访问静态变量和实例变量的两种方 ...

  6. 【Java线程】深入理解Volatile关键字和使用

    目录 背景 volatile原理 volatile特性 可见性 有序性 原子性 使用场景 背景 理解volatile底层原理之前,首先介绍关于缓存一致性协议的知识. 背景:计算机在执行程序时,每条指令 ...

  7. 深入理解final关键字

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  8. [C#.NET 拾遗补漏]10:理解 volatile 关键字

    要理解 C# 中的 volatile 关键字,就要先知道编译器背后的一个基本优化原理.比如对于下面这段代码: public class Example {public int x;public voi ...

  9. 通过反汇编来理解restrict关键字

    一次难忘的面试经历 多年前,一次互联网某厂实习生的面试题,题目的代码片段很简单,如下: 1 #include 2 int main()3 {4 int *restrict pInt = (int*)m ...

  10. 深入理解synchronized关键字

    2019独角兽企业重金招聘Python工程师标准>>> synchronized是并发编程中重要的使用工具之一,我们必须学会使用并且掌握它的原理. 概念及作用 JVM自带的关键字,可 ...

最新文章

  1. 解决json包含html标签无法显示的问题
  2. easyui-window窗口不遮挡_眼睛是心灵的窗口、佩戴舒适又时尚的米家防蓝光护目镜 Pro...
  3. 朋友圈广告助手_腾讯社交广告代理附近推跟朋友圈广告对比优势
  4. c++ 航空管理系统_浅谈航站楼能源管理系统的设计与应用
  5. apt-get 与 apt-cache使用
  6. 昨天做的事情和今天需要做的事情
  7. jenkins手把手教你从入门到放弃02-jenkins在Windows系统安装与配置
  8. 知乎:“我们不主动收集数据”;滴滴被打乘客怂了;三星太子归位 | CSDN极客头条...
  9. NoSQL数据库之国产开源产品:SequoiaDB 分析前言
  10. python操作sql_Python操作MSSQL
  11. mysql字符集修改(ubuntu)
  12. Swift 数据类型(三)
  13. idea导入opencv教程
  14. 安装安全防护软件有助于保护计算机不受侵害,安装安全防护软件有助于保护计算机不受病毒侵害。...
  15. 高德地图定位误差_高德地图定位api以及导航和定位 位置的偏差
  16. Unable to add window android.view.ViewRootImpl$W@c1bf05d -- permission denied for window type 2003
  17. (一)淘宝首页的代码(周六一天敲出来的)html结构展示
  18. 【数理知识】狄利克雷函数 dirac(t)
  19. Deferred Decal
  20. Win7多用户下开机只显示一个用户

热门文章

  1. execv bad address
  2. 你是想读书,还是想读完书?
  3. Apache Calcite介绍
  4. winedit使用教程_latex及winedit入门指导教程.pdf
  5. 将网页,网站(HTML,php,css)上传浏览器,实现输入网址即可访问(保姆级教学)
  6. 常用的OpenCV函数速查
  7. 网页底部小鱼游动特效
  8. python光棍节快乐_光棍节快乐的祝福语QQ【17句】
  9. 京东商品如何批量修改?
  10. 故事系列之一:围棋世界里看天赋和勤奋