00055-hard-union-to-intersection

Back

利用函数参数逆变的特性

type UnionToIntersection<U> = (U extends any ? (a: U) => void : never) extends (
  a: infer I
) => void
  ? I
  : never; 

Solution by Vampirelee #32599

递归解法,同时利用了联合条件类型拆分(T extends T)

type UnionToIntersection<T, F = T> = T extends T
  ? Array<F> extends Array<T>
    ? T
    : T & UnionToIntersection<Exclude<F, T>>
  : never;
type I = UnionToIntersection<"foo" | 42 | true>;

Solution by WangyongAnna #32416

// 你的答案

type UnionToIntersection<U extends any> = (U extends U ? (x: U) => any : never) extends (x: infer R) => any ? R : never

Solution by laqudee #32359

type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends ((arg: infer I) => any) ? I : never

Solution by smileboyi #27483

// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
type UnionToIntersection<U> = (
  U extends never ? never : (k: U) => void
) extends (k: infer I) => void
  ? I
  : never

Solution by HubooDeclan #27035

type ToUnionOfFunction<T> =
  T extends T
  ? (a: T) => unknown
  : never;
type UnionToIntersection<U> =
  ToUnionOfFunction<U> extends (a: infer U) => unknown
  ? U
  : never;

type t0 = 
  // ^?
  ToUnionOfFunction<(() => 'foo') | ((i: 42) => true)>


type t1 = 
  // ^?
  UnionToIntersection<(() => 'foo') | ((i: 42) => true)> 

这道题的关键点在于使用了一个非常巧妙的性质,举个例子

type Bar<T> = 
	T extends {
		a: (x: infer U) => void;
		b: (x: infer U) => void;
	}
 ? U : never

这个例子乍一看有点奇怪,因为我们竟然推断了两次 infer U ,但是实际上会有如下面的结果,就成功构造出了一个 intersection

type T1 = Bar<{
	a: (x: string) => void;
	b: (x: string) => void;
}> // string

type T2 = Bar<{
	a: (x: string) => void;
	b: (x: number) => void;
}> // string & number

Solution by kiki-zjq #25953

// your answers
type UnionToIntersection<U> = (U extends U ? (arg: U) => any : never) extends (
  arg: infer P
) => any
  ? P
  : never

Solution by studymachiney #24954

type UnionToIntersection<U> = (U extends U ? (arg: U) => any : never) extends ((arg: infer P) => any) ? P : never;  //先用函数分拆,再收窄并集成参数。。。我大受震撼

Solution by E-uler #24668

type UnionToIntersection<U> = (U extends any ? (a: U) => any : never) extends ((b: infer A) => any) ? A : never;

Solution by sabercc #23852


// 触发分派 > 函数参数交叉
type UnionToIntersection<U> = (U extends U ? (args: U) => never : never) extends (args: infer X) => never ? X : never;

Solution by litangmm #23546

// your answers
type UnionToIntersection<U> =
  (U extends U
    ? (arg: U) => void
    : never
  ) extends (arg: infer R) => void
    ? R
    : never

Solution by snakeUni #23385

// 你的答案
type UnionToIntersection<U> = (U extends U ? (arg: U) => unknown : never) extends (arg: infer R) => unknown ? R : never

Solution by jxhhdx #22814

type UnionToIntersection<U> =
  (U extends U
    ? (arg: U) => void
    : never
  ) extends (arg: infer R) => void
    ? R
    : never

The reason from type-inference-in-conditional-types :

The following example demonstrates how multiple candidates for the same type variable in co-variant positions causes a union type to be inferred:

type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type T10 = Foo<{ a: string; b: string }>; // string
type T11 = Foo<{ a: string; b: number }>; // string | number

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

type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
  ? U
  : never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string & number

Solution by drylint #22188

// your answers
// good
type UnionToFunctionUnion<U> = 
  U extends unknown
    ? (arg: U) => unknown
    : never

type UnionToIntersection<T> = (T extends any  ? (arg: T) => any : never) extends (arg: infer Rest) => any 
                ? Rest 
                : never

// bad
type Filter<T, K> = T extends K ? never : T

type UnionToArray<U, R extends any[] = [],  A = U> = [U] extends [never] ? R : U extends any ? UnionToArray<Filter<A, U>, [...R, U]> : never

type ArrayToObject<T extends any []> = T extends [infer F, ...infer Rest] ? F & ArrayToObject<Rest> : {}

type UnionToIntersection<T> = ArrayToObject<UnionToArray<T>>

Solution by 437204933 #21994

type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends
  (arg: infer R) => any ? R : never

Solution by jgjgill #21890

type UnionToIntersection<U> =
(U extends U ? (x: U) => unknown : never) extends (x: infer R) => unknown
? R
: never

Solution by so11y #21001

type UnionToIntersection<U> = 
  (U extends any ? (arg: () => U) => any : never) extends (arg: () => infer P) => any
    ? P
    : never

Solution by pengzhanbo #20477

// your answers
type UnionToIntersection<U extends any> =
    (U extends any ? (arg:U) => any : never) extends (arg:infer R) => any    
    ? R
    : never ;

Solution by YqxLzx #20348

// your answers
type UnionToIntersection<U> = (U extends U ? (a: U) => void : never) extends (
  a: infer R
) => void
  ? R
  : never;

Solution by fengjinlong #20164

type UnionToFunctionUnion<U> = 
  U extends unknown
    ? (arg: U) => unknown
    : never

type UnionToIntersection<U> = 
  UnionToFunctionUnion<U> extends (arg: infer Arg) => unknown
    ? Arg
    : never

In general, we take advantage of the feature of ts in the inference of conditional types, inferring the same type from multiple positions. If these positions are contravariant, the final type is stitched by intersecting.

Therefore, the key is to find a simple type that has the contravariant position.

The Function is such a type that the position of its arguments is the contravariant position.

In the UnionToFunctionUnion, we make use of distributive conditional type to map union of types into union of functions with each type as the arg of each function.

Then, in the UnionToIntersection, we try to infer all args in each functions into one arg in one function. As arg postion in function is contra-variant, so they are inferred and stitched by intersecting, and we return it. That's all.

Solution by zhaoyao91 #20129

// 1. 将Union类型变成函数的联合函数类型
type ToUnionFunction<T> = T extends unknown ? (arg: T) => void : never;

// 2. 联合函数的类型,怎么就变成了交叉参数的一个函数?
//    ts对应PR:https://github.com/Microsoft/TypeScript/pull/21496
type UnionToIntersection<U> = ToUnionFunction<U> extends (arg: infer Args) => void ? Args : never;

Solution by CaoXueLiang #18930

type UnionToIntersection<U> = (U extends any ? (args: U) => void : never) extends (args: infer A) => void ? A : never


type A = (i: 42) => true
type B = () => 'foo'
type a = ((a: A) => any) | ((a: B) => any) extends (a: infer U) => any ? U : never // A & B

Solution by milletlovemouse #18298

// your answers
type ToFunc<T> = T extends any
  ? (arg: T) => void
  : never

type UnionToIntersection<U> = ToFunc<U> extends (args: infer Arg) => void
                                            ? Arg
                                            : never;

Solution by jiaaoMario #16963

// your answers
type ToFunc<T> = T extends any
  ? (arg: T) => void
  : never

type UnionToIntersection<U> = ToFunc<U> extends (arg: infer Arg) => void
  ? Arg
  : never

Solution by seho-dev #16732

// your answers

type ToFunc<T> = T extends any
  ? (arg: T) => void
  : never

type UnionToIntersection<U> = ToFunc<U> extends (arg: infer Arg) => void
  ? Arg
  : never

Solution by humandetail #16377


/**
 * 1. `U extends unknown ? (args: U) => void : never` 将生成函数的联合函数类型 比如:((args: true) => void) | ((args: 42) => void) | ((args: "foo") => void)
 * 2. `extends (args: infer T) => void ? T : never` 将联合函数聚合成一个函数,👆的联合函数将变为交叉参数的函数 (args: 'foo' & 42 & true) => void
 * 3. 返回出函数参数
 * 
 * 知识点:第二步骤 联合函数的类型,怎么就变成了交叉参数的一个函数? ts对应PR:https://github.com/Microsoft/TypeScript/pull/21496
 * 即:在 逆变位置 的同一类型变量中的多个候选会被 推断 成 交叉类型。【函数参数是逆变的,而对象属性是协变的。】
 * 关于 逆变与协变 https://jkchao.github.io/typescript-book-chinese/tips/covarianceAndContravariance.html
 */

type UnionF = U extends unknown ? (args: U) => void : never type UnionToIntersection = UnionF extends (args: infer T) => void ? T : never

Solution by tonghuitop #15679

// your answers
type UnionToIntersection<
    U extends any> =
    (U extends any ? (arg:U)=>any : never) extends (arg:infer R)=>any    
    ? R
    : never ;

Solution by justBadProgrammer #15644

// 你的答案
type UnionToIntersection<U> = (U extends any ? (x:U) => any : never) extends (x:infer A) => any ? A : never

Solution by userakf #15115

type UnionToIntersection<U> = (U extends U ? ((a: U) => void) : never) extends ((a: infer R) => void) ? R : never

Solution by a145789 #13918

type UnionToIntersection<U> =( U extends any ? (p: U) => any: never) extends (p: infer R) => any ? R : never

Solution by wqs576222103 #12947