Skip to content

Instantly share code, notes, and snippets.

@tokland
Last active September 14, 2020 22:14
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 tokland/8750311b712f69659f1dce88e08714ee to your computer and use it in GitHub Desktop.
Save tokland/8750311b712f69659f1dce88e08714ee to your computer and use it in GitHub Desktop.
Flatten a union into a plain type with a union of all keys (optional for non-common keys)
type AllKeysOfUnion<U> = U extends any ? keyof U : never;
type NonCommonKeysOfUnion<U> = Exclude<AllKeysOfUnion<U>, keyof U>;
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type UnionWithKeys<U, K extends keyof any> = U extends any
? { [Key in K]: Key extends keyof U ? U[Key] : never }
: never;
export type FlattenUnion<U> = PartialBy<
UnionWithKeys<U, AllKeysOfUnion<U>>,
NonCommonKeysOfUnion<U>
>;
// Compile-time testing helpers
type IsEqual<T1, T2> = [T1] extends [T2] ? ([T2] extends [T1] ? true : false) : false;
const assertEqualTypes = <T1, T2>(_eq: IsEqual<T1, T2>): void => {};
const assertValueHasType = <T>(_value: T): void => {};
// Usage example
type UnionType =
| { type: "type1"; key1: number; key2: symbol }
| { type: "type2"; key1: string }
| { type: "type3"; key2: string };
type FlattenUnionType = FlattenUnion<UnionType>;
assertEqualTypes<keyof FlattenUnionType, "type" | "key1" | "key2">(true);
assertEqualTypes<FlattenUnionType["type"], "type1" | "type2" | "type3">(true);
assertEqualTypes<FlattenUnionType["key1"], number | string | undefined>(true);
assertEqualTypes<FlattenUnionType["key2"], symbol | string | undefined>(true);
assertValueHasType<FlattenUnionType>({ type: "type1" });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment