You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

59 lines
1.9KB

  1. /**
  2. Convert a union type to an intersection type using [distributive conditional types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
  3. Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
  4. @example
  5. ```
  6. import {UnionToIntersection} from 'type-fest';
  7. type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
  8. type Intersection = UnionToIntersection<Union>;
  9. //=> {the(): void; great(arg: string): void; escape: boolean};
  10. ```
  11. A more applicable example which could make its way into your library code follows.
  12. @example
  13. ```
  14. import {UnionToIntersection} from 'type-fest';
  15. class CommandOne {
  16. commands: {
  17. a1: () => undefined,
  18. b1: () => undefined,
  19. }
  20. }
  21. class CommandTwo {
  22. commands: {
  23. a2: (argA: string) => undefined,
  24. b2: (argB: string) => undefined,
  25. }
  26. }
  27. const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
  28. type Union = typeof union;
  29. //=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
  30. type Intersection = UnionToIntersection<Union>;
  31. //=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
  32. ```
  33. */
  34. export type UnionToIntersection<Union> = (
  35. // `extends unknown` is always going to be the case and is used to convert the
  36. // `Union` into a [distributive conditional
  37. // type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
  38. Union extends unknown
  39. // The union type is used as the only argument to a function since the union
  40. // of function arguments is an intersection.
  41. ? (distributedUnion: Union) => void
  42. // This won't happen.
  43. : never
  44. // Infer the `Intersection` type since TypeScript represents the positional
  45. // arguments of unions of functions as an intersection of the union.
  46. ) extends ((mergedIntersection: infer Intersection) => void)
  47. ? Intersection
  48. : never;