-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
130 lines (123 loc) · 3.42 KB
/
index.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/**
* @see https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
*
* Big thanks to @dfreeman (Dan Freeman) for helping with this!
* @see https://discordapp.com/channels/480462759797063690/484421406659182603/694852926572593253
*/
/**
* Retrieves the predicate type of a user-defined type predicate function.
* Like `ReturnType<T>`, but for type predicate functions.
*
* @see https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates
*
* @example
* ```ts
* type Foo = PredicateType<(subject: unknown) => is { foo: string }>;
* // => { foo: string }
* ```
*/
export type PredicateType<T extends (subject: unknown) => unknown> = T extends (
subject: unknown
) => subject is infer Type
? Type
: never;
/**
* Create a tuple of type predicate functions from the specified types.
*
* @example
* ```ts
* type StringNumber = Predicates<[string, number]>;
* // => [
* // (v: unknown) => v is string,
* // (v: unknown) => v is number
* // ]
* ```
*/
type Predicates<T extends unknown[]> = {
[K in keyof T]: (subject: unknown) => subject is T[K];
} &
unknown[];
/**
* Converts a union type (`A | B`) to an intersection type (`A & B`).
*/
type UnionToIntersection<T> = (T extends T ? (p: T) => void : never) extends (
p: infer U
) => void
? U
: never;
/**
* Gets all values of a tuple as a union type (`A | B`).
*
* @example
* ```ts
* type StringOrNumber = UnionOf<[string, number]>; // => string | number
* ```
*/
type UnionOf<T extends unknown[]> = T[keyof T & number];
/**
* Gets all values of a tuple as an intersection type (`A & B`).
*
* @example
* ```
* type FooAndBar = IntersectionOf<[{ foo: string }, { bar: string }]>;
* ```
*/
type IntersectionOf<T extends unknown[]> = UnionToIntersection<UnionOf<T>>;
/**
* Combines a list of type predicate functions into a union type guard.
*
* @see https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types
*
* @example
* ```ts
* type Foo = { foo: boolean; baz: number };
* type Bar = { bar: symbol; baz: string };
*
* declare function isFoo(v: unknown): v is Foo;
* declare function isBar(v: unknown): v is Bar;
*
* const isFooOrBar = isSome(isFoo, isBar);
* // => (subject: unknown) => subject is Foo | Bar
*
* if (isFooOrBar(x)) {
* x.baz; // => string | number
*
* if ('foo' in x) {
* x.foo; // => boolean
* } else {
* x.bar; // => symbol
* }
* }
* ```
*/
export const isSome = <Types extends unknown[]>(
...predicates: Predicates<Types>
) => (subject: unknown): subject is UnionOf<Types> =>
predicates.some(is => is(subject));
/**
* Combines a list of type predicate functions into an intersection type guard.
*
* @see https://www.typescriptlang.org/docs/handbook/advanced-types.html#intersection-types
*
* @example
* ```ts
* type Foo = { foo: boolean; baz: number };
* type Bar = { bar: symbol; baz: string };
*
* declare function isFoo(v: unknown): v is Foo;
* declare function isBar(v: unknown): v is Bar;
*
* const isFooAndBar = isEvery(isFoo, isBar);
* // => (subject: unknown) => subject is Foo & Bar
*
* if (isFooAndBar(x)) {
* x.foo; // => boolean
* x.bar; // => symbol
* x.baz; // => never (no such thing as `string & number`)
* }
* ```
*/
export const isEvery = <Types extends unknown[]>(
...predicates: Predicates<Types>
) => (subject: unknown): subject is IntersectionOf<Types> =>
predicates.every(is => is(subject));