-
Notifications
You must be signed in to change notification settings - Fork 14
/
types.ts
237 lines (218 loc) · 6.24 KB
/
types.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import { Internal } from './internal/types.ts'
/**
* The failure case of a Result.
* It has a list of Errors.
*/
type Failure = {
success: false
errors: Array<Error>
}
/**
* The success case of a Result.
* It has a generic T as the data.
* It also has an empty list of errors for convenience.
*/
type Success<T = void> = {
success: true
data: T
errors: []
}
/**
* The output of a computation that might fail.
*/
type Result<T = void> = Success<T> | Failure
/**
* Merges the data types of a list of objects.
* @example
* type MyObjs = [
* { a: string },
* { b: number },
* ]
* type MyData = MergeObjects<MyObjs>
* // ^? { a: string, b: number }
*/
type MergeObjects<Objs extends unknown[], output = {}> = Objs extends [
infer first,
...infer rest,
]
? MergeObjects<rest, Internal.Prettify<Omit<output, keyof first> & first>>
: output
/**
* A composable async function that catches failures.
*/
type Composable<T extends (...args: any[]) => any = (...args: any[]) => any> = (
...args: Parameters<T>
) => Promise<Result<Awaited<ReturnType<T>>>>
/**
* A composable async function with schema validation at runtime.
*/
type ComposableWithSchema<O> = Composable<
(input?: unknown, environment?: unknown) => O
>
/**
* Extract the type of the returned data when a Composable is successful.
*/
type UnpackData<T extends Composable> = Extract<
Awaited<ReturnType<T>>,
{ success: true }
>['data']
/**
* Extracts the types of successful data returned by multiple Composables.
*/
type UnpackAll<List extends Composable[]> = {
[K in keyof List]: UnpackData<List[K]>
}
/**
* A Composable that represents the sequential execution of multiple Composables.
* The return type is a tuple with all results on the success data.
* This type can resolve to a FailToCompose when the composition won't type-check.
*/
type SequenceReturn<Fns extends unknown[]> = Fns extends [
Composable<(...args: infer P) => any>,
...any,
]
? Composable<(...args: P) => UnpackAll<Fns>>
: Fns
/**
* A Composable that represents the sequential execution of multiple Composables.
* The return type is the success data of the last function in the chain.
* This type can resolve to a FailToCompose when the composition won't type-check.
*/
type PipeReturn<Fns extends unknown[]> = Fns extends [
Composable<(...args: infer P) => any>,
...any,
]
? Composable<(...args: P) => UnpackData<Extract<Last<Fns>, Composable>>>
: Fns
/**
* Determines whether a sequence of Composables can be composed sequentially.
*/
type CanComposeInSequence<
Fns extends any[],
Arguments extends any[] = [],
> = Fns extends [Composable<(...a: infer PA) => infer OA>, ...infer restA]
? restA extends [
Composable<
(firstParameter: infer FirstBParameter, ...b: infer PB) => any
>,
...unknown[],
]
? Internal.IsNever<Awaited<OA>> extends true
? Internal.FailToCompose<never, FirstBParameter>
: Awaited<OA> extends FirstBParameter
? Internal.EveryElementTakes<PB, undefined> extends true
? CanComposeInSequence<
restA,
[...Arguments, Composable<(...a: PA) => OA>]
>
: Internal.EveryElementTakes<PB, undefined>
: Internal.FailToCompose<Awaited<OA>, FirstBParameter>
: [...Arguments, Composable<(...a: PA) => OA>]
: never
/**
* Determines whether a sequence of Composables can be composed in parallel.
*/
type CanComposeInParallel<
Fns extends any[],
OriginalFns extends any[] = Fns,
> = Fns extends [Composable<(...a: infer PA) => any>, ...infer restA]
? restA extends [Composable<(...b: infer PB) => infer OB>, ...infer restB]
? Internal.SubtypesTuple<PA, PB> extends [...infer MergedP]
? CanComposeInParallel<
[Composable<(...args: MergedP) => OB>, ...restB],
OriginalFns
>
: Internal.FailToCompose<PA, PB>
: Internal.ApplyArgumentsToFns<OriginalFns, PA>
: never
/**
* Transforms a record of Composables into a tuple of their return types.
*/
type RecordToTuple<T extends Record<string, Composable>> =
Internal.RecordValuesFromKeysTuple<T, Internal.Keys<T>>
/**
* A serializable error object.
*/
type SerializableError = {
exception: Error
message: string
name: string
path: string[]
}
/**
* The serializable output of a Result.
*/
type SerializableResult<T> =
| Success<T>
| { success: false; errors: SerializableError[] }
/**
* The object used to validate either input or environment when creating composables with a schema.
*/
type ParserSchema<T extends unknown = unknown> = {
safeParse: (a: unknown) =>
| {
success: true
data: T
}
| {
success: false
error: { issues: { path: Array<string | number>; message: string }[] }
}
}
/**
* Returns the last element of a tuple type.
*/
type Last<T extends readonly unknown[]> = T extends [...infer _I, infer L]
? L
: never
/**
* A Composable that branches based on the output of another Composable.
*/
type BranchReturn<
SourceComposable extends Composable,
Resolver extends (
...args: any[]
) => Composable | null | Promise<Composable | null>,
> = CanComposeInSequence<
[SourceComposable, Composable<Resolver>]
> extends Composable[]
? Awaited<ReturnType<Resolver>> extends null
? SourceComposable
: CanComposeInSequence<
[SourceComposable, Awaited<ReturnType<Resolver>>]
> extends [Composable, ...any]
? Composable<
(
...args: Parameters<
CanComposeInSequence<
[SourceComposable, Awaited<ReturnType<Resolver>>]
>[0]
>
) => null extends Awaited<ReturnType<Resolver>>
?
| UnpackData<SourceComposable>
| UnpackData<Extract<Awaited<ReturnType<Resolver>>, Composable>>
: UnpackData<Extract<Awaited<ReturnType<Resolver>>, Composable>>
>
: CanComposeInSequence<[SourceComposable, Awaited<ReturnType<Resolver>>]>
: CanComposeInSequence<[SourceComposable, Composable<Resolver>]>
export type {
BranchReturn,
CanComposeInParallel,
CanComposeInSequence,
Composable,
ComposableWithSchema,
Failure,
Last,
MergeObjects,
ParserSchema,
PipeReturn,
RecordToTuple,
Result,
SequenceReturn,
SerializableError,
SerializableResult,
Success,
UnpackAll,
UnpackData,
}