Skip to content

Instantly share code, notes, and snippets.

@gustavoguichard
Last active January 6, 2023 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gustavoguichard/b17d9e052458bad88233565dc7d3d7df to your computer and use it in GitHub Desktop.
Save gustavoguichard/b17d9e052458bad88233565dc7d3d7df to your computer and use it in GitHub Desktop.
Union to Tuple
type Specify<T> = T extends any ? (k: T) => void : never
// Real intersection: UnionToIntersection<`c${string}` | 'car'> will become `c${string}`
type UnionToIntersection<T> = Specify<T> extends (a: infer A) => void ? A : never;
// Transforms 'a' | 'b' into ((f: "a") => void) & ((f: "b") => void)
type UnionToOverloads<U> = UnionToIntersection<Specify<U>>;
// Still figuring out why this happens
// ((f: "a") => void) & ((f: "b") => void) will become "b"
type LastOfUnion<U> = UnionToOverloads<U> extends (a: infer A) => void ? A : never;
// Only unions won't conform to this
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type PopUnion<T> = {
result: LastOfUnion<T>,
rest: Exclude<T, LastOfUnion<T>>
}
// Grabs the last of union as a way to infer last and pops it with exclude
type UnionToTuple<T, output extends unknown[] = []> = IsUnion<T> extends true
? UnionToTuple<PopUnion<T>['rest'], [PopUnion<T>['result'], ...output]>
: [T, ...output];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment