对于ts中联合类型+extends的理解

2,041 阅读2分钟

问题的引发

type-challenge 中有一个 IsUnion 的题:判断一个类型是联合类型。

先上答案(issues里找的):

type case1 = IsUnion<string>  // false
type case2 = IsUnion<string|number>  // true
type case3 = IsUnion<[string|number]>  // false

type IsUnion<T, F = T> =
    T extends T
        ? Array<F> extends Array<T>
            ? false
            : true
        : never

这个题如果没有对 联合类型+extends 的深入理解,应该是很难做出来的,而且做出来,也不代表你就理解了。

先简单理解一下

type Res1 = string|number extends string|number ? true : false // true
type Res2 = string extends string|number ? true : false // true
type Res3 = string|number extends string ? true : false // false

看完这段代码,相信你会觉得很简单,但是细细分析,又可以有多种不同的理解。先提出一个疑问:在Res1中,ts内部是怎么运算的?

Res1应该是拆成下面几个步骤来计算的:

  1. string|number extends string|number ? true : false 需要拆分成 string extends string|number ? true : falsenumber extends string|number ? true : false 两步。
  2. 拆分出来的表达式的结果分别是true和true,结果进行联合,还是true,所以Res1的结果就是true。

T extends T 作用 —— 拆分

type Res<T> = T extends T ? true : false

对于任何类型来说 T extends T 都是成立的,但 T extends T 有什么特别的作用呢? 它的作用在于联合类型,回想一下 Res 的运算——拆分,T extends T 的作用也是拆分!

对于 IsUnion 的理解

type IsUnion<T, F = T> =
    T extends T
        ? Array<F> extends Array<T>
            ? false
            : true
        : never

在经过 T extends T 的拆分后,? Array<F> extends Array<T> 这一行中的 T 已经成为了联合类型中的某个单独的类型了,而 F 依然还是 完备的联合类型,这就导致了 Array<F> extends Array<T> 不成立。

所以,IsUnion 的解题核心,就是通过 T extends T 进行拆分。

拆分后,就好办了,剩下就解法千千万,不做过多阐述。

相关资源

type-challenge