-
Notifications
You must be signed in to change notification settings - Fork 0
/
union-to-intersection.ts
68 lines (68 loc) · 2 KB
/
union-to-intersection.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* This utility is easily comparable to function overloading in TypeScript.
*
* ```ts
* // helper for overloading with generics. Not robust enough for overloads that change the return type.
* type CreateOverload<Union, Returns = void> = UnionToIntersection<
* Union extends unknown ? (u: Union) => Returns : never
* >
*
* type BarProps = { kind: 0; enabled: boolean } | { kind: 1; fn: () => void }
* type Bar = CreateOverload<BarProps>
*
* const bar: Bar = (u) => {
* if (u.kind === 0) {
* u.enabled
* } else {
* u.fn()
* }
* }
* //
* // ((u: {
* // kind: 0;
* // enabled: boolean;
* // }) => void) & ((u: {
* // kind: 1;
* // fn: () => void;
* // }) => void)
* ```
*
* @example
* `arg` is narrowed as ts checks the function sequentially from top to bottom, stopping at the first match.
* ```ts
* function foo(arg: number): number
* function foo(arg: string): string
* function foo(arg: string | number): string {
* return arg
* }
* ```
* `foo` can be represented using an interface:
* ```ts
* interface Foo {
* (arg: number): number
* (arg: string): string
* (arg: string | number): string
* }
* ```
* ```ts
* declare function Some<T>(): T
* declare const foo: Foo
*
* //@ts-expect-error - `arg` in `foo` must be a string or number
* foo(Some<boolean>()) // 🚨✅
* foo(Some<40>()) // (arg: number) => number (+2 overloads) // ✅
* foo(Some()) // (arg: number) => number (+2 overloads) // ✅
* foo(Some<number>()) // (arg: number) => number // ✅
* foo(Some<string>()) // (arg: string) => string (+2 overloads) // ✅
* foo(Some<string | number>()) // (arg: string | number) => string (+2 overloads) // ✅
* ```
* you could overload arrow functions like this as well, but you need a type parameter to do it:
* ```ts
* const foo = <T>(arg: T) => arg // const foo: Foo ✅
* ```
*/
export type UnionToIntersection<Union> = (
Union extends unknown ? (u: Union) => void : never
) extends (u: infer Intersection) => void
? Intersection
: never