问题的引发
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应该是拆成下面几个步骤来计算的:
string|number extends string|number ? true : false需要拆分成string extends string|number ? true : false和number extends string|number ? true : false两步。- 拆分出来的表达式的结果分别是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 进行拆分。
拆分后,就好办了,剩下就解法千千万,不做过多阐述。