我不知道为什么测试用例中UnionToTuple<undefined | void | 1>
需要结果是[void, 1],我的答案是[undefined, void, 1]。
type UnionToTuple<T, R = UnionToIntersection<T extends T ? (arg: T) => void : never>> = R extends (arg: infer Arg) => void
? R extends ((arg: Arg) => void) & infer Rest
? [Arg, ...UnionToTuple<T, Rest>]
: never
: []
为了过测试用例必须手动排除undefined
type UnionToTuple<T, R = UnionToIntersection<T extends T ? (arg: T) => void : never>> = R extends (arg: infer Arg) => void
? R extends ((arg: Arg) => void) & infer Rest
? [...[Arg extends undefined ? never : Arg], ...UnionToTuple<T, Rest>]
: never
: []
Solution by 2083335157 #35042
先说结论:对于函数联合类型推断函数参数时,结果会是 这两个函数参数类型的交叉类型,相关概念:协变
type FnA = (a: 1) => void
type FnB = (b: 2) => void
type Result1 = (FnA | FnB ) extends (a: infer R) => void ? R : never; // 结果会是 1 & 2, 即never
那如果函数参数是本身就是函数呢
type FnA = (a: (arg: 1) => void) => void
type FnB = (b: (arg: 2) => void) => void
type Result1 = (FnA | FnB ) extends (a: infer R) => void ? R : never;
// 结果当然是这两个函数的交叉类型 ((arg: 1) => void) & ((arg: 2) => void)
那对交叉类型的两个函数进行参数推断结果会是什么呢?
type Result2 = ((arg: 1) => void) & ((arg: 2) => void) extends (arg: infer R) => void ? R: never;
// 结果会是 2, 个人猜测1或者2都是符合条件的,可能根据编译器的优先级有关,在我本机上结果是2
这一步我们就提取了联合类型的某一个类型了,再进行递归,就可以把剩下的类型全部提出出来。
本题解答:
type UnionToTuple<T, Last = UnionLast<T>> = [T] extends [never]
? []
: Last extends T
? [...UnionToTuple<Exclude<T, Last>>, UnionLast<Last>]
: [];
type IntersectionFunction<T> = (
T extends any ? (a: (b: T) => void) => void : never
) extends (a: infer R) => void
? R
: never;
type UnionLast<T> = IntersectionFunction<T> extends (a: infer R) => void
? R
: never;
Solution by Vampirelee #32625
type UnionToIntersection<U> =
(U extends U ? (arg: U) => unknown : never) extends
((arg: infer I) => unknown) ? I : never
type LastOfUnion<U> =
UnionToIntersection<
(U extends unknown ? (arg: U) => unknown : never)
> extends (arg: infer I) => unknown ? I : never
type IsAny<T> = [T] extends [never] ?
false :
T extends (1 & T) ?
true : unknown extends T ?
true : false
type IsUnion<T, Copy = T> =
[T] extends [never] ?
false :
T extends T ?
[Copy] extends [T] ?
// using isAny here because of
// specific type-cases
IsAny<T> extends true ?
true :
false :
true :
false
type UnionToTupleImpl<U, R extends unknown[] = []> =
[U] extends [never] ?
R :
UnionToTupleImpl<Exclude<U, LastOfUnion<U>>, [...R, LastOfUnion<U>]>
type UnionToTuple<U> =
[U] extends [never] ?
never :
IsUnion<U> extends false ?
U :
UnionToTupleImpl<U>
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type ExtractValuesOfTuple<T extends any[]> = T[keyof T & number]
type cases = [
Expect<Equal<UnionToTuple<'a' | 'b'>['length'], 2>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<'a' | 'b'>>, 'a' | 'b'>>,
// Expect<Equal<ExtractValuesOfTuple<UnionToTuple<'a'>>, 'a'>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<any>>, any>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<undefined | void | 1>>, void | 1>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<any | 1>>, any | 1>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<any | 1>>, any>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<'d' | 'f' | 1 | never>>, 'f' | 'd' | 1>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<[{ a: 1 }] | 1>>, [{ a: 1 }] | 1>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<never>>, never>>,
Expect<Equal<ExtractValuesOfTuple<UnionToTuple<'a' | 'b' | 'c' | 1 | 2 | 'd' | 'e' | 'f' | 'g'>>, 'f' | 'e' | 1 | 2 | 'g' | 'c' | 'd' | 'a' | 'b'>>,
]
Solution by gearonixx #31874
type UnionToIntersection
type GetLastUnion
type UnionToTuple<T,Last=GetLastUnion
Solution by DoubleWoodLin #28580
//需要了解性质:多个函数交集的返回值类型只取最后一个!(This is Important!)
//例如:
// type Intersepted = (() => 'a') & (() => 'b') & (() => 'c')
// type Last = Intersepted extends () => infer R ? R : never // 'c'
//参考:https://github.com/type-challenges/type-challenges/issues/21658#issue-1523555097
/**并集转交集 */
type UnionToIntersection<T> = (T extends T ? (args: T) => any : never) extends (args: infer P) => any ? P : never; // a | b | c ==> a & b & c
/**联合类型最后一个 */
type UnionLast<T> = (UnionToIntersection<T extends T ? () => T : never>) extends () => infer R ? R : never; // a | b | c ==> (()=>a) | (()=>b) | (()=>c) ==> (()=>a) & (()=>b) & (()=>c) ==> c
type UnionToTuple<T> = [T] extends [never] ? [] : [UnionLast<T>, ...UnionToTuple<Exclude<T, UnionLast<T>>>];
参考:https://github.com/type-challenges/type-challenges/issues/21658#issue-1523555097
Solution by E-uler #24894
// your answers
type UnionToIntersection<U> = (U extends U ? (x: U) => unknown : never) extends (x: infer R) => unknown ? R : never;
export type UnionToTuple<T> =
UnionToIntersection<
T extends any ? () => T : never
> extends () => infer ReturnType
? [...UnionToTuple<Exclude<T, ReturnType>>, ReturnType]
: [];
Solution by jxhhdx #23736
type UnionToFnInserction<T> = (T extends any ? (arg: () => T) => any : never) extends (arg: infer P) => any ? P : never
type UnionToTuple<T, A extends any[] = []> = UnionToFnInserction<T> extends () => infer R ? UnionToTuple<Exclude<T, R>, [R, ...A]> : A
Solution by snakeUni #23660
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
type IsUnion<T, B = T> = T extends B ? ([B] extends [T] ? false : true) : false
type NumberUnionToTuple<U, N extends number[] = [], Result extends any[] = []> =
IsUnion<U> extends false
? [U] extends [never]
? Result
: [...Result, U]
: IsEqual<U, N['length']> extends true
? [...Result, U]
: NumberUnionToTuple<
U extends N['length'] ? never : U,
[...N, 0],
N['length'] extends U ? [...Result, N['length']] : Result
>
type t0 = NumberUnionToTuple<2> // [2]
type t1 = NumberUnionToTuple<2 | 3 | 5> // [2, 3, 5]
type t2 = NumberUnionToTuple<100 | 200 | 300 | 500> // [100, 200, 300, 500]
type t3 = NumberUnionToTuple<10 | 5 | 20 | 888 | 30 | 66 | 500> // [5, 10, 20, 30, 66, 500, 888]
Solution by TKBnice #23588
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
type IsUnion<T, B = T> = T extends B ? ([B] extends [T] ? false : true) : false
type UnionToTuple<U, N extends number[] = [], Result extends any[] = []> =
IsUnion<U> extends false
? [U] extends [never]
? Result
: [...Result, U]
: IsEqual<U, N['length']> extends true
? [...Result, U]
: UnionToTuple<
U extends N['length'] ? never : U,
[...N, 0],
N['length'] extends U ? [...Result, N['length']] : Result
>
type t0 = UnionToTuple<2> // [2]
type t1 = UnionToTuple<2 | 3 | 5> // [2, 3, 5]
type t2 = UnionToTuple<100 | 200 | 300 | 500> // [100, 200, 300, 500]
type t3 = UnionToTuple<10 | 5 | 20 | 888 | 30 | 66 | 500> // [5, 10, 20, 30, 66, 500, 888]
Solution by TKBnice #23587
type UnionToCross<T> = (T extends T ? (s: () => T) => void : never) extends (
s: infer R
) => void
? R
: never;
type GetCrossLast<T> = T extends () => infer R ? R : never;
type UnionToTuple<T, Result extends Array<any> = []> = [T] extends [never]
? Result
: [
...UnionToTuple<Exclude<T, GetCrossLast<UnionToCross<T>>>>,
GetCrossLast<UnionToCross<T>>
];
Solution by so11y #21225
// your answers
type UnionToIntersectionFn<U> = (
U extends unknown ? (k: () => U) => void : never
) extends (k: infer I) => void ? I : never;
type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer I
? I : never;
type Prepend<Tuple extends unknown[], First> = [First, ...Tuple];
type UnionToTuple<
Union,
T extends unknown[] = [],
Last = GetUnionLast<Union>
> = [Union] extends [never]
? T
: UnionToTuple<Exclude<Union, Last>, Prepend<T, Last>>;
Solution by YqxLzx #21113
type UnionToCross<T> = (T extends any ? (x: T) => any : never) extends (x: infer T) => any ? T : never
type UnionToTuple<T> = UnionToCross<
T extends any
? () => T
: never
> extends () => infer R
? [...UnionToTuple<Exclude<T, R>>,R]
: []
type Test = UnionToTuple<'a' | 'b'>
// ^?
Solution by XkSuperCool #20764
type UnionToFnInsertion<T> = (T extends any
? (arg: () => T) => any : never) extends (arg: infer P) => any
? P
: never
type UnionToTuple<T> = UnionToFnInsertion<T> extends () => infer R
? [...UnionToTuple<Exclude<T, R>>, R]
: []
Solution by pengzhanbo #20475
// 参考:https://github.com/type-challenges/type-challenges/issues/10191
// 1. 将联合类型转换成对应的函数交叉类型 (arg的参数是函数,返回的类型才是交叉类型,其他的都是never)
type UnionToIntersectionFn<U> = (U extends unknown ? (arg: () => U) => void : never) extends (arg: infer I) => void ? I : never;
// 2. 获取联合类型的最后一个类型
// (() => "a") & (() => "b") & (() => "c")
// 在获取函数的返回值上,函数重载和函数交叉类型是一样的
type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer I ? I : never;
// 3. 联合类型转换为元组
type UnionToTuple<U, Last = GetUnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];
Solution by CaoXueLiang #19059
// your answers
// an intersection of function types is considered the same as an overloaded function with multiple call signatures
// and when TypeScript using type inference on an overloaded function type, It just uses the last call signature and ignores all the rest of them
type UnionToFnInserction<T> = (T extends any ? (arg: () => T) => any : never) extends (arg: infer P) => any ? P : never
type UnionToTuple<T, A extends any[] = []> = UnionToFnInserction<T> extends () => infer R ? UnionToTuple<Exclude<T, R>, [R, ...A]> : A
Solution by ljq108 #18153
// your answers
/**
* UnionToFunc<1 | 2> => ((arg: 1) => void | (arg: 2) => void)
*/
type UnionToFunc<T> = T extends unknown ? (arg: T) => void : never
/**
* UnionToIntersection<1 | 2> = 1 & 2
*/
type UnionToIntersection<U> = UnionToFunc<U> extends (arg: infer Arg) => void
? Arg
: never
/**
* LastInUnion<1 | 2> = 2
*/
type LastInUnion<U> = UnionToIntersection<UnionToFunc<U>> extends (x: infer L) => void
? L
: never
type UnionToTuple<T, L = LastInUnion<T>> = [L] extends [never]
? []
: [...UnionToTuple<Exclude<T, L>>, L]
Solution by humandetail #16449
// your answers
type UnionToIntersection<
U> =
(U extends any ? (arg: U)=>any : never) extends (arg: infer R)=>any ? R : never ;
type LastOfUnion<
U> =
UnionToIntersection<U extends any ? ()=>U : never> extends ()=>infer R ? R : never;
type UnionToTuple<
U,
Last extends any = LastOfUnion<U>> =
[U] extends [never]
? []
: [Last,...UnionToTuple<Exclude<U,Last>>]
Solution by justBadProgrammer #15869
// your answers
type UnionToFn<T> = (
T extends unknown ? (k:() => T) => void : never
) extends( (k: infer R) => void) ? R : never
type UnionToTuple<T, P extends any[] = []> = UnionToFn<T> extends () => infer R ? Exclude<T, R> extends never ? [...P, R] : UnionToTuple<Exclude<T, R>, [...P, R]> : never;
Solution by FeelyChau #13938
type getLastElm<T> = ((T extends T ? (a: (a: T) => void) => void : never) extends ((a: infer R) => void) ? R : never) extends (a: infer L) => void ? L : never
type UnionToTuple<T, Tuples extends unknown[] = []> = getLastElm<T> extends never ? Tuples : UnionToTuple<Exclude<T, getLastElm<T>>, [...Tuples, getLastElm<T>]>
Solution by a145789 #13928
type UnionToIntersectionFn<TUnion> = (
TUnion extends TUnion ? (union: () => TUnion) => void : never
) extends (intersection: infer Intersection) => void
? Intersection
: never;
type LastUnion<TUnion> = UnionToIntersectionFn<TUnion> extends () => infer Last
? Last
: never;
type UnionToTuple<
TUnion,
TResult extends Array<unknown> = []
> = TUnion[] extends never[]
? TResult
: UnionToTuple<
Exclude<TUnion, LastUnion<TUnion>>,
[...TResult, LastUnion<TUnion>]
>;
Solution by michaltarasiuk #12602
// your answers
// 'a' | 'b' | 'c' => ()=>'a' & ()=>'b' & ()=>'c'
// 知识点:函数参数类型是逆变的
type UnionToIntersectionFn<U> = (U extends unknown ?
(k: () => U) => void :
never) extends (k: infer I) => void ?
I :
never;
// ()=>'a' & ()=>'b' & ()=>'c' => 'c'
// 知识点1:函数交叉类型与函数重载本质上一样
// 知识点2: https://github.com/Microsoft/TypeScript/issues/24275#issuecomment-390701982
type GetLastReturnType<U> = UnionToIntersectionFn<U> extends ()=>infer R ?
R :
never;
type UnionToTuple<U, T extends Array<unknown> = []> = [U] extends [never] ?
T :
UnionToTuple<Exclude<U, GetLastReturnType<U>>, [...T, GetLastReturnType<U>]>;
Solution by Joyee691 #11541
type UnionToTuple<T>
= [T] extends [never] ? []
: ReturnType<<
L extends (T extends T ? (_: Promise<T>) => T : never) extends (_: infer L) => T ? Awaited<L>
: never
>() => [...UnionToTuple<Exclude<T, L>>, L]>
Solution by teamchong #11473
type Union2IntersectionFn<T> = (
T extends unknown ? (k:() => T) => void : never
) extends( (k: infer R) => void) ? R : never
type GetUnionLast<U> = Union2IntersectionFn<U> extends () => infer I
? I : never;
type UnionToTuple<T, R extends any[] = []> = [T] extends [never] ? R : UnionToTuple<Exclude<T,GetUnionLast<T> >, [GetUnionLast<T>,...R]>
Solution by wqs576222103 #11061
// your answers
type UnionToIntersectionFn<U> = (
U extends unknown ? (k: () => U) => void : never
) extends (k: infer I) => void ? I : never;
type GetUnionLast<U> = UnionToIntersectionFn<U> extends () => infer I
? I : never;
type Prepend<Tuple extends unknown[], First> = [First, ...Tuple];
type UnionToTuple<
Union,
T extends unknown[] = [],
Last = GetUnionLast<Union>
> = [Union] extends [never]
? T
: UnionToTuple<Exclude<Union, Last>, Prepend<T, Last>>;
Solution by astak16 #10191
type UnionToIntersection<T> =( T extends T ? (params: T) => any : never) extends (params: infer P) => any ? P : never
type UnionToTuple<T, Res extends any[] = []>
= UnionToIntersection<T extends any ? () => T : never> extends ()=> infer ReturnType ? UnionToTuple<Exclude<T,ReturnType>,[...Res,ReturnType]>: Res
Solution by EGyoung #8549
/**
* 从 Unoin 类型里面取出最后一个
* 首先把传入的 Unoin 类型分配构造成函数 Unoin:
* a | b ==> (() => a) | (() => b)
* 然后丢进 UnoinToIntersection 处理成交叉类型
* (() => a) & (() => b) 这个会被解释为函数重载
* 然后再条件语句中,函数重载取最后一个值,所以拿到
* 了传入 Unoin 的最后一个值
*/
type LastOf<T> = UnionToIntersection<
T extends any ? () => T : never
> extends () => infer R
? R
: never
// 这里是如何去掉 undefined | void | 1 中的undefined的呢
// 因为 undefined 可以赋值给 void,所以在
// Exclude<undefine, void> 的时候就会被干掉
type UnionToTuple<T, L = LastOf<T>, N = IsNever<T>> = true extends N
? []
: Push<UnionToTuple<Exclude<T, L>>, L>
Solution by godlanbo #7551
// 730 - Union to Tuple
type UnionToIntersection = ( U extends unknown ? (arg: U) => 0 : never ) extends (arg: infer I) => 0 ? I : never;
type LastInUnion = UnionToIntersection< U extends unknown ? (x: U) => 0 : never
extends (x: infer L) => 0 ? L : never;
type UnionToTuple<U, Last = LastInUnion> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];
Solution by Carefree-happy #7091
type OmitX<T, K> = T extends T
? T extends K
? never
: T
: never;
type UnionToTuple<T, K = T> = [T] extends [never]
? []
: T extends T
? [T, ...UnionToTuple<OmitX<K, T>>]
: []
Solution by Luncher #3968
// union to intersection of functions
type UnionToIoF<U> =
(U extends any ? (k: (x: U) => void) => void : never) extends
((k: infer I) => void) ? I : never
// return last element from Union
type UnionPop<U> = UnionToIoF<U> extends { (a: infer A): void; } ? A : never;
// prepend an element to a tuple.
type Prepend<U, T extends any[]> =
((a: U, ...r: T) => void) extends (...r: infer R) => void ? R : never;
type UnionToTupleRecursively<Union, Result extends any[]> = {
1: Result;
0: UnionToTupleRecursively_<Union, UnionPop<Union>, Result>;
// 0: UnionToTupleRecursively<Exclude<Union, UnionPop<Union>>, Prepend<UnionPop<Union>, Result>>
}[[Union] extends [never] ? 1 : 0];
type UnionToTupleRecursively_<Union, Element, Result extends any[]> =
UnionToTupleRecursively<Exclude<Union, Element>, Prepend<Element, Result>>;
type UnionToTuple<U> = UnionToTupleRecursively<U, []>;
Solution by Jcanno #3112
// https://github.com/type-challenges/type-challenges/issues/737
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (
x: infer U
) => any
? U
: never
// get last Union: LastUnion<1|2> => 2
// ((x: A) => any) & ((x: B) => any) is overloaded function then Conditional types are inferred only from the last overload
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
type LastUnion<T> = UnionToIntersection<
T extends any ? (x: T) => any : never
> extends (x: infer L) => any
? L
: never
type UnionToTuple<T, Last = LastUnion<T>> = [T] extends [never]
? []
: [...UnionToTuple<Exclude<T, Last>>, Last]
Solution by myNameIsDu #2835