typescript泛型疑难杂症——extend

extend以泛型传入自动分发成联合类型

Posted by Xshellv on 2021-10-06

先看下面的这个例子:

1、 条件类型

1
2
3
type IType = 'a' | 'b' | 'c'
type IType2 = 'a' | 'c'
type Diff1 = IType extends IType2 ? never : IType // ?

2、 分布式条件类型

1
2
type Diff2<T, U> = T extends U ? never : T
type T1 = Diff2<'a' | 'b' | 'c', 'a' | 'c'> // ?

3、非裸类型的分布式

1
2
type Diff3<T, U> = [T] extends [U] ? never : T
type T2 = Diff3<'a' | 'b' | 'c', 'a' | 'c'> // ?

如果你能正确的说出以上的答案,那么恭喜你可以跳过,否则我们一探究竟:

条件类型

条件类型是ts中非常强大的功能,形如:type R = T extends U ? X : Y,表示如果 TU 的子类,则返回类型为 X,否则为 Y 。和三元表达式相似。为了区分下面的分布式条件类型我们需要着重强调下这里的 T 不是以泛型传入的

根据以上:

1
type Diff1 = 'a' | 'b' | 'c' extends 'a' | 'c' ? never : 'a' | 'b' | 'c' // 'a' | 'b' | 'c'

分布式条件类型

在结合联合类型使用时(只针对 extends 左边的联合类型),分布式条件类型会被自动分发成联合类型

分布式条件类型 等价于
string extends T ? A : B string extends T ? A : B
(string | number) extends T ? A : B (string extends T ? A : B) | (number extends T ? A : B)
(string | number | boolean) extends T ? A : B (string extends T ? A : B) | (number extends T ? A : B) | (boolean extends T ? A : B)

根据以上:

1
type T1 = Diff<'a','a'|'c'> | Diff<'b','a'|'c'> | Diff<'c','a'|'c'> //  never | 'b' | never  -> 'b'

划重点:
在官方文档中提到,分布式条件类型是有前提的。条件类型中待检查的类型(即 extends 左边的类型)必须是裸类型(naked type parameter。即没有被诸如数组,元组或者函数包裹

根据以上:

1
2
3
4
type Diff3<T, U> = [T] extends [U] ? never : T
type T2 = Diff3<'a' | 'b' | 'c', 'a' | 'c'> // ?
// 等价于:
type T2 = ['a' | 'b' | 'c'] extends ['a' | 'c'] ? never : 'a' | 'b' | 'c' // 'a' | 'b' | 'c'

参考文章