看一道经典体操题:如何实现一个UnionToIntersection?具体来说,这个问题有三个子问题:联合类型的分配律、逆变位置、逆变和协变。在递归求解一个问题的过程中,我们递归给出一个解。//测试用例类型U=UnionToIntersection<{a:string}|{b:number}>//输入U={a:string}&{b:number};注意输入不能是原始类型的联合类型,因为原始类型的交集类型是never。这时候就需要利用这两种类型和功能的奇妙碰撞了。关节类型的分配律我们知道关节类型遵循分配律。当我们传递一个联合类型时,例如{a:string}|{b:number}转换成类型typeT,typeT<{a:string}|{b:number}>实际上等同于类型T<{a:string}>|输入T<{b:数字}>。以下是官网的解释。那么如果我们这样写typeT:typeToUnionOfFunction=Textendsany?(x:T)=>任何:从不;也就是说,我们构造一个函数,将传入的联合类型作为参数。我们将上面的测试用例传递给这个类型:typeFunctions=ToUnionOfFunction<{a:string}|{b:number}>这时候结果就变成了:typeFunctions=|((x:{a:字符串})=>任何)|((x:{b:number})=>any)由于分配律,我们得到两个不同参数的函数的联合类型。这时候我们怎么得到交集类型呢?铛铛!向下看!类型UnionToIntersection=ToUnionOfFunctionextends(x:inferP)=>any?P:从不;在我们解开ToUnionOfFunction之后它是(((x:{a:string})=>any)|(((x:{b:number})=>any))extends(x:inferP)=>any?P:never.TypeScript的这个PR中有一句话:multiplecandidatesforsametypevariableincontra-variantpositionscausesmultiplecandidatesinthesametypevariableinthecontravariantposition会被推断为交集类型,基于这个属性,我们的UnionToIntersection满足测试用例。到底什么是反转位置?首先记住一句话:函数参数是逆变的,而对象属性是协变的。逆变位置的变量意思是变量是一个函数的参数。什么是逆变和协变?!在《深入理解 TypeScript》的逆变协方差章节有详细介绍。《深入理解 TypeScript》是一本好书,建议你多看看。OK,解决了这三个问题,我们就可以理解这个经典的pr了问题。