TypeScript细碎知识点:关于type Exclude<T, U extends T> = T extends U ? never : T;

题目:实现内置的Exclude <T,U>,从 T 中排除可分配给 U 的那些类型

type x = string | number | boolean

type y = string | number

type c = MyExclude<x, y>

const b: c = true

解答

type MyExclude<T, U> = T extends U ? never : T 

疑问为什么 联合类型T extends U 后 “ T extends U ? never : T ”  T 不再是原来的类型,而是替换掉U的类型。

这个反直觉结果的原因就是所谓的分配条件类型(Distributive Conditional Types)

分配条件类型:对于使用extends关键字的条件类型(即上面的三元表达式类型),如果extends前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。分配律是指,将联合类型的联合项拆成单项,分别代入条件类型,然后将每个单项代入得到的结果再联合起来,得到最终的判断结果。

举例子说明

type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'>  // A3的类型是 string | number

该例中,extends的前参为T,T是一个泛型参数。在A3的定义中,给T传入的是'x'和'y'的联合类型'x' | 'y',满足分配律,于是'x'和'y'被拆开,分别代入P<T>

P<'x' | 'y'> => P<'x'> | P<'y'>

'x'代入得到

'x' extends 'x' ? string : number => string

'y'代入得到

'y' extends 'x' ? string : number => number

然后将每一项代入得到的结果联合起来,得到string | number。

所以题目中 type MyExclude<T, U extends T> = T extends U ? never : T; 也满足了分配率。大概如下

type c = MyExclude<x, y>
/*
 (string extends string | number ? never : string) |  //结果: never
 (number extends string | number ? never : number) |  //结果: never
 (boolean extends string | number ? never : boolean)  //结果: boolean
*/

总之,满足两个要点即可适用分配律:第一,参数是泛型类型,第二,代入参数的是联合类型

posted on 2024-11-18 17:34  梁飞宇  阅读(44)  评论(0)    收藏  举报