59 lines
1.9 KiB
Plaintext
59 lines
1.9 KiB
Plaintext
|
/**
|
||
|
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).
|
||
|
|
||
|
Inspired by [this Stack Overflow answer](https://stackoverflow.com/a/50375286/2172153).
|
||
|
|
||
|
@example
|
||
|
```
|
||
|
import {UnionToIntersection} from 'type-fest';
|
||
|
|
||
|
type Union = {the(): void} | {great(arg: string): void} | {escape: boolean};
|
||
|
|
||
|
type Intersection = UnionToIntersection<Union>;
|
||
|
//=> {the(): void; great(arg: string): void; escape: boolean};
|
||
|
```
|
||
|
|
||
|
A more applicable example which could make its way into your library code follows.
|
||
|
|
||
|
@example
|
||
|
```
|
||
|
import {UnionToIntersection} from 'type-fest';
|
||
|
|
||
|
class CommandOne {
|
||
|
commands: {
|
||
|
a1: () => undefined,
|
||
|
b1: () => undefined,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class CommandTwo {
|
||
|
commands: {
|
||
|
a2: (argA: string) => undefined,
|
||
|
b2: (argB: string) => undefined,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const union = [new CommandOne(), new CommandTwo()].map(instance => instance.commands);
|
||
|
type Union = typeof union;
|
||
|
//=> {a1(): void; b1(): void} | {a2(argA: string): void; b2(argB: string): void}
|
||
|
|
||
|
type Intersection = UnionToIntersection<Union>;
|
||
|
//=> {a1(): void; b1(): void; a2(argA: string): void; b2(argB: string): void}
|
||
|
```
|
||
|
*/
|
||
|
export type UnionToIntersection<Union> = (
|
||
|
// `extends unknown` is always going to be the case and is used to convert the
|
||
|
// `Union` into a [distributive conditional
|
||
|
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
|
||
|
Union extends unknown
|
||
|
// The union type is used as the only argument to a function since the union
|
||
|
// of function arguments is an intersection.
|
||
|
? (distributedUnion: Union) => void
|
||
|
// This won't happen.
|
||
|
: never
|
||
|
// Infer the `Intersection` type since TypeScript represents the positional
|
||
|
// arguments of unions of functions as an intersection of the union.
|
||
|
) extends ((mergedIntersection: infer Intersection) => void)
|
||
|
? Intersection
|
||
|
: never;
|