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