乐趣区

关于前端:TS经典类型体操联合类型如何转为转交叉类型需要知道三个点分配律逆变位置逆变和协变

来看一个经典的类型体操问题:如何实现一个 UnionToIntersection?

具体地说,这个问题有三个子问题:联结类型的分配律、逆变地位、逆变和协变。咱们在递归地解决问题的过程中,递归地给出解答。

// test case
type U = UnionToIntersection<{a: string} | {b: number}> // type U = {a: string} & {b: number};

留神,输出不能是原始类型的联结类型,因为原始类型的穿插类型是 never

这个时候,就要用到这两个类型与函数的微妙碰撞了。

联结类型的分配律

咱们晓得联结类型听从分配律。当咱们将一个联结类型如 {a: string} | {b: number} 传入一个类型 type T<U> 时,type T<{a: string} | {b: number}> 实际上等价于 type T<{a: string}> | type T<{b: number}>

上面这个是官网的解释。

那如果咱们把 type T<U> 写成这个样子:

type ToUnionOfFunction<T> = T extends any ? (x: T) => any : never;

即,咱们结构一个将传入的联结类型作为参数的函数。咱们将下面的 test case 传入这个类型:

type Functions = ToUnionOfFunction<{a: string} | {b: number}> 

这个时候,后果就变成了:

type Functions =
    | ((x: { a: string}) => any)
    | ((x: { b: number}) => any)

因为分配律,咱们失去了两个参数不同的函数的联结类型。

这个时候咱们怎么失去穿插类型呢?

锵锵!看上面!

type UnionToIntersection<T> =  ToUnionOfFunction<T> extends (x: infer P) => any ? P : never;

咱们将ToUnionOfFunction<T> 解开后便是 (((x: { a: string}) => any) | ((x: { b: number}) => any) ) extends (x: infer P) => any ? P : never

在 TypeScript 的这个 PR 中有一句话:

multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred.

即:在 逆变地位 的同一类型变量中的多个候选会被推断成穿插类型。

基于这个性质,咱们的 UnionToIntersection<T> 便满足测试用例了。

逆变地位到底是个什么?

首先记住一句话:函数参数是逆变的,而对象属性是协变的。

变量处于逆变地位就是 这个变量是一个函数的参数

到底什么是逆变和协变?!

在《深刻了解 TypeScript》的 逆变和协变 一节中有具体介绍。

《深刻了解 TypeScript》是本好书呀,倡议多看看。

OK,这三个问题解决完之后,咱们对这个经典问题也算是搞懂了。

本文首发于我的博客:https://callanbi.top/post/dan… 转载请注明出处。

退出移动版