type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends (arg: infer I) => void ? I : never;
Solution by wendao-liu #35166
type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends (arg: infer R) => void ? R : never
Solution by ouzexi #34200
利用函数参数逆变的特性
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