16-新语法 infer extends 是如何简化类型编程的
课程
1
如何阅读本小册
已学完
学习时长: 2分6秒
2
为什么说 TypeScript 的火爆是必然?
已学完
学习时长: 4分28秒
3
TypeScript 类型编程为什么被叫做类型体操?
已学完
学习时长: 9分16秒
4
TypeScript 类型系统支持哪些类型和类型运算?
已学完
学习时长: 19分27秒
5
套路一:模式匹配做提取
已学完
学习时长: 41分32秒
6
套路二:重新构造做变换
已学完
学习时长: 39分51秒
7
套路三:递归复用做循环
学习时长: 42分22秒
8
套路四:数组长度做计数
学习时长: 27分58秒
9
套路五:联合分散可简化
学习时长: 19分36秒
10
套路六:特殊特性要记清
学习时长: 36分41秒
11
类型体操顺口溜
学习时长: 27分26秒
12
TypeScript 内置的高级类型有哪些?
学习时长: 31分59秒
13
真实案例说明类型编程的意义
学习时长: 34分36秒
14
类型编程综合实战一
学习时长: 25分55秒
15
类型编程综合实战二
学习时长: 29分9秒
16
新语法 infer extends 是如何简化类型编程的
学习时长: 11分14秒
17
原理篇:逆变、协变、双向协变、不变
学习时长: 11分31秒
18
原理篇:编译 ts 代码用 tsc 还是 babel?
学习时长: 19分49秒
19
原理篇:实现简易 TypeScript 类型检查
学习时长: 47分51秒
20
原理篇:如何阅读 TypeScript 源码
学习时长: 19分27秒
21
原理篇:一些特殊情况的说明
已学完
学习时长: 12分39秒
22
小册总结
学习时长: 2分32秒
23
加餐:3 种类型来源和 3 种模块语法
学习时长: 17分6秒
24
加餐:用 Project Reference 优化 tsc 编译性能
已学完
学习时长: 4分47秒
juejin_logo copyCreated with Sketch.

我们知道,TypeScript 支持 infer 来提取类型的一部分,通过模式匹配的方式。

比如元组类型提取最后一个元素的类型:

type Last<Arr extends unknown[]> = 
    Arr extends [...infer rest,infer Ele]
        ? Ele 
        : never;

比如函数提取返回值类型:

type GetReturnType<Func extends Function> = 
    Func extends (...args: any[]) => infer ReturnType 
        ? ReturnType 
        : never;

比如字符串提取一部分,然后替换:

type ReplaceStr<
    Str extends string,
    From extends string,
    To extends string
> = Str extends `${infer Prefix}${From}${infer Suffix}` 
        ? `${Prefix}${To}${Suffix}` : Str;

模式匹配就是通过一个类型匹配一个模式类型,需要提取的部分通过 infer 声明一个局部变量,这样就能从局部变量里拿到提取的类型。

infer 的模式匹配用法还是挺好理解的。

但是 infer 有一个问题,比如这样:

从 string 数组中提取的元素,默认会推导为 unknown 类型,这就导致了不能直接把它当 string 用:

那怎么办呢?

之前的处理方式是这样的:

加一层判断,这样 Last 就推导为 string 类型了。

或者也可以和 string 取交叉类型:

这样也可以作为 string 来用。

但是我们明明知道这里就是 string,却还需要 & string 或者 xxx extends string 来转换一次,这也太麻烦了。

TS 也知道有这个问题,所以在 4.7 就引入了新语法:infer extends。

现在我们可以这样写:

infer 的时候加上 extends 来约束推导的类型,这样推导出的就不再是 unknown 了,而是约束的类型。

试一下

这个语法是 TS 4.7 引入的,在 4.8 又完善了一下。

比如这样一个类型:

type NumInfer<Str> = 
    Str extends `${infer Num extends number}`
        ? Num
        : never;

在 4.7 的时候推导结果是这样:

而 4.8 就是这样了:

也就是说 4.7 的时候推导出的就是 extends 约束的类型,但是 4.8 的时候,如果是基础类型,会推导出字面量类型。

有了这个语法之后,除了能简化类型编程的逻辑之外,也能实现一些之前实现不了的功能:

比如提取枚举的值的类型:

enum Code {
    a = 111,
    b = 222,
    c = "abc"
}

我们都是这样写:

但是有的值明明是数字,却被作为了字符串,所以要再处理一下,转换成数字类型,这时候就可以用 infer extends 了:

type StrToNum<Str> =
  Str extends `${infer Num extends number}`
    ? Num
    : Str

做完 string 到 number 的转换,就拿到了我们想要的结果:

这就是 infer extends 的第二个作用。

处理 string 转 number 之外,也可以转 boolean、null 等类型:

试一下

总结

Typescript 支持 infer 类型,可以通过模式匹配的方式,提取一部分类型返回。

但是 infer 提取出的类型是 unknown,后面用的时候需要类似和 string 取交叉类型,或者 xxx extends string 这样的方式来转换成别的类型来用。这样比较麻烦。

所以 TS 4.7 实现了 infer extends 的语法,可以指定推导出的类型,这样简化了类型编程。

而且,infer extends 还可以用来做类型转换,比如 string 转 number、转 boolean 等。

要注意的是,4.7 的时候,推导出的只是 extends 约束的类型,比如 number、boolean,但是 4.8 就能推导出字面量类型了,比如 1、2、true、false 这种。

有了 infer extends,不但能简化类型编程,还能实现一些之前很难实现的类型转换。

留言
Ctrl + Enter