kind-adt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts ADDED
@@ -0,0 +1,1455 @@
1
+ /******************
2
+ * Main functions *
3
+ ******************/
4
+ /**
5
+ * Generate constructors and related functions (e.g., match functions) for an ADT. These include:
6
+ * - `ADT.<Tag>`: Constructor for the ADT.
7
+ * - `ADT.is<Tag>`: Type guard function for the ADT.
8
+ * - `ADT.unwrap`: Deconstructor for the ADT. This function extracts the fields of the ADT into a
9
+ * TypeScript tuple (i.e., an array).
10
+ * - `ADT.unwrap<Tag>`: Deconstructor for the ADT with a specific tag. If the passed ADT has a
11
+ * different tag, it will throw an error.
12
+ * - `ADT.if<Tag>`: Conditional deconstructor for the ADT. Similar to Rust’s `if let` syntax.
13
+ * - `ADT.match`: A match function to pattern match the ADT. This function requires the return type
14
+ * of each case to be the same.
15
+ * - `ADT.matchW`: Same as `ADT.match`, but allows the return type of each case to be different.
16
+ * @param variants The variants of the ADT. If not provided, a proxy object will be returned.
17
+ * @returns
18
+ *
19
+ * @see {@linkcode Data} for details on how to create an ADT.
20
+ */
21
+ export function make<F extends Tagged | TypeLambda<never, Tagged>>(
22
+ variants?: readonly Instantiate<F>["_tag"][],
23
+ ): Instantiate<F> extends infer Type extends Tagged ?
24
+ Spread<
25
+ { readonly [Tag in Type["_tag"] as Tag]: Constructor<F, Tag> },
26
+ {
27
+ readonly [Tag in Type["_tag"] as `is${Tag}`]: (
28
+ adt: Type,
29
+ ) => adt is FilterTagged<Type, Tag>;
30
+ },
31
+ { readonly unwrap: Deconstructor<Type> },
32
+ {
33
+ readonly [Tag in Type["_tag"] as `unwrap${Tag}`]: Deconstructor<
34
+ FilterTagged<Type, Tag>
35
+ >;
36
+ },
37
+ {
38
+ readonly [Tag in Type["_tag"] as `if${Tag}`]: ConditionalDeconstructorOf<
39
+ F,
40
+ Tag
41
+ >;
42
+ },
43
+ {
44
+ readonly match: MatcherOf<F>;
45
+ readonly matchW: MatcherOfW<F>;
46
+ }
47
+ >
48
+ : never;
49
+
50
+ /**
51
+ * Extract the fields of an ADT.
52
+ * @param adt The ADT to unwrap.
53
+ * @returns
54
+ */
55
+ export function unwrap<T extends Tagged>(adt: T): ExtractFields<T>;
56
+
57
+ /**************
58
+ * Main types *
59
+ **************/
60
+ /**
61
+ * A tagged type with a `_tag` property.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * // Create a tagged type
66
+ * type _ = Tagged<"A", [string, number]>;
67
+ * // => { readonly _tag: "A"; readonly _0: string; readonly _1: number }
68
+ * ```
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Extract a variant from an ADT
73
+ * type Option<T> = Data<{
74
+ * Some: [T];
75
+ * None: [];
76
+ * }>;
77
+ * type Some<T> = Extract<Option<T>, Tagged<"Some">>;
78
+ * // ^?: type Some<T> = { readonly _tag: "Some"; readonly _0: T }
79
+ * ```
80
+ */
81
+ export type Tagged<
82
+ Tag extends string = string,
83
+ Fields = readonly unknown[],
84
+ > = Merge<
85
+ { readonly _tag: Tag },
86
+ (
87
+ [
88
+ Exclude<keyof Fields, "__labels">,
89
+ IsNever<Exclude<keyof Fields, "__labels">>,
90
+ ] extends [number, false] ?
91
+ Exclude<Fields, "__labels">
92
+ : Fields
93
+ ) extends infer Fields ?
94
+ { readonly [I in IndexOf<Fields> as `_${I}`]: Fields[I] }
95
+ : never
96
+ >;
97
+
98
+ /**
99
+ * Create an ADT with tagged types.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { type Data, type Tagged, make } from "kind-adt";
104
+ * import type { Arg0, HKT } from "hkt-core";
105
+ *
106
+ * // A Simple ADT
107
+ * export type Option<T> = Data<{
108
+ * // ^?: { readonly _tag: "Some"; readonly _0: T } | { readonly _tag: "None" }
109
+ * Some: [T];
110
+ * None: [];
111
+ * }>;
112
+ * // Extract a variant from an ADT
113
+ * export type Some<T> = Extract<Option<T>, Tagged<"Some">>;
114
+ * // ^?: type Some<T> = { readonly _tag: "Some"; readonly _0: T }
115
+ * // Generate constructors and match functions for the ADT
116
+ * export const Option = make<OptionHKT>();
117
+ * interface OptionHKT extends HKT { // <- Lift the ADT to HKT
118
+ * return: Option<Arg0<this>>;
119
+ * }
120
+ * // Use the ADT
121
+ * function safeDiv(n1: number, n2: number) {
122
+ * // ^?: (n1: number, n2: number) => Option<number>
123
+ * if (n2 === 0) return Option.None;
124
+ * return Option.Some(n1 / n2);
125
+ * }
126
+ * const result = Option.match(safeDiv(1, 2), {
127
+ * // ^?: string
128
+ * Some: (n) => `Result: ${n}`,
129
+ * None: () => {
130
+ * throw new Error("Division by zero");
131
+ * },
132
+ * });
133
+ * console.log(result); // Result: 0.5
134
+ * ```
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * import { type Data, make } from "kind-adt";
139
+ * import type { Arg0, Arg1, HKT2 } from "hkt-core";
140
+ *
141
+ * // You can add optional labels to the constructor to improve readability of type information
142
+ * export type Result<T, E> = Data<{
143
+ * Ok: [value: T]; // <- With labels
144
+ * Err: [error: E]; // <- With labels
145
+ * }>;
146
+ * export const Result = make<ResultHKT>();
147
+ * interface ResultHKT extends HKT2 {
148
+ * return: Result<Arg0<this>, Arg1<this>>;
149
+ * }
150
+ *
151
+ * // When you hover over `Result.Err`, you will a much more readable type information:
152
+ * Result.Err("Oops!");
153
+ * // ^?: <never, string>(error: string) => Result<never, string>
154
+ * // Without labels, the type information will be less readable:
155
+ * // Result.Err("Oops!");
156
+ * // ^?: <never, string>(args_0: string) => Result<never, string>
157
+ *
158
+ * // This also applies to the generated `ADT.match` function:
159
+ * Result.match(Result.Err("Oops!"), {
160
+ * Ok: (v) => `Value: ${v}`,
161
+ * // ^?: (value: never) => string
162
+ * // Without labels, you get `(args_0: never) => string`
163
+ * Err: (e) => `Error: ${e}`,
164
+ * // ^?: (error: string) => string
165
+ * // Without labels, you get `(args_0: string) => string`
166
+ * });
167
+ * ```
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * import { type Data, make } from "kind-adt";
172
+ * import type { Arg0, Arg1, HKT2 } from "hkt-core";
173
+ *
174
+ * // Constraints on type parameters are also supported
175
+ * export type Entry<K extends string | number, V> = Data<{
176
+ * Entry: [key: K, value: V];
177
+ * }>;
178
+ * export const { Entry } = make<EntryHKT>();
179
+ * interface EntryHKT extends HKT2<string | number, unknown> {
180
+ * return: Entry<Arg0<this>, Arg1<this>>;
181
+ * }
182
+ *
183
+ * // TypeScript will issue an error if you pass the wrong type of arguments to the constructor:
184
+ * Entry(null, "John");
185
+ * // ~~~~
186
+ * // Argument of type 'null' is not assignable to parameter of type 'string | number'.
187
+ * ```
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * import { type Data, make } from "kind-adt";
192
+ * import type { Arg0, HKT } from "hkt-core";
193
+ *
194
+ * // A sugared syntax for ADTs with only one object argument
195
+ * export type Tree<T> = Data<{
196
+ * Empty: {};
197
+ * Node: { value: T; left: Tree<T>; right: Tree<T> };
198
+ * }>;
199
+ * // This expands to:
200
+ * // type Tree<T> =
201
+ * // | { readonly _tag: "Empty" }
202
+ * // | { readonly _tag: "Node"; readonly _0: { value: T; left: Tree<T>; right: Tree<T> } };
203
+ * // NOTE: kind-adt will treat `{}` as `[]` in this syntax.
204
+ *
205
+ * export const { Empty, Node, match: matchTree } = make<TreeHKT>();
206
+ * interface TreeHKT extends HKT {
207
+ * return: Tree<Arg0<this>>;
208
+ * }
209
+ *
210
+ * const depth: <T>(tree: Tree<T>) => number = matchTree({
211
+ * Empty: () => 0,
212
+ * Node: ({ left, right }) => 1 + Math.max(depth(left), depth(right)),
213
+ * });
214
+ *
215
+ * console.log(depth(Node(1, Node(2, Node(3, Empty, Empty), Empty))); // 3
216
+ * ```
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * import { type Data, make } from "kind-adt";
221
+ * import type { Arg0, HKT } from "hkt-core";
222
+ *
223
+ * // TypeScript sometimes has trouble handling recursive ADTs
224
+ * type Tree<T> = Data<{
225
+ * // ~~~~
226
+ * // Type alias 'Tree' circularly references itself.
227
+ * Empty: [];
228
+ * Node: [value: T, left: Tree<T>, right: Tree<T>];
229
+ * }>;
230
+ *
231
+ * // You might wonder why TypeScript handles the recursive ADT in the previous example correctly,
232
+ * // but fails in this example. The reason is that TypeScript lazily evaluates certain types,
233
+ * // including interfaces, object literal types and function return types, but eagerly evaluates
234
+ * // the rest (e.g., tuple types and type aliases defined by `type`).
235
+ * // In the previous example, we quoted the `Tree` type inside an object literal type, which is
236
+ * // lazily evaluated.
237
+ * // However, in this example, we quote `Tree<T>` in a tuple type, which is eagerly evaluated.
238
+ *
239
+ * // kind-adt provides an alternative syntax for such cases:
240
+ * type Tree<T> = Data<{
241
+ * Empty: [];
242
+ * Node: { 0: T; 1: Tree<T>; 2: Tree<T> };
243
+ * }>;
244
+ * // Instead of using a tuple type, we use an object type with only numeric keys.
245
+ * // This expands to:
246
+ * // type Tree<T> =
247
+ * // | { readonly _tag: "Empty" }
248
+ * // | { readonly _tag: "Node"; readonly _0: T; readonly _1: Tree<T>; readonly _2: Tree<T> };
249
+ * // NOTE: kind-adt treats object with only numeric keys as tuple types, instead of the syntax
250
+ * // sugar for ADTs with only one object argument.
251
+ *
252
+ * // However, such syntax does not support labels. kind-adt also provides a special syntax for
253
+ * // recursive ADTs with labels:
254
+ * type Tree<T> = Data<{
255
+ * Empty: [];
256
+ * Node: {
257
+ * // The `__labels` property can be a labeled tuple containing any type, we only use the labels
258
+ * __labels: [value: void, left: void, right: void];
259
+ * 0: T;
260
+ * 1: Tree<T>;
261
+ * 2: Tree<T>;
262
+ * };
263
+ * }>;
264
+ * // This expands to the same type as the previous example.
265
+ *
266
+ * export const Tree = make<TreeHKT>();
267
+ * interface TreeHKT extends HKT {
268
+ * return: Tree<Arg0<this>>;
269
+ * }
270
+ *
271
+ * const depth: <T>(tree: Tree<T>) => number = Tree.match({
272
+ * Empty: () => 0,
273
+ * Node: (_, left, right) => 1 + Math.max(depth(left), depth(right)),
274
+ * // ^?: (value: T, left: Tree<T>, right: Tree<T>) => number
275
+ * // Labels are preserved in the match function
276
+ * });
277
+ * ```
278
+ */
279
+ export type Data<Variants extends Record<string, unknown>> = ValueOf<{
280
+ [Tag in keyof Variants & string]: Variants[Tag] extends (
281
+ readonly unknown[] // Regular ADTs
282
+ ) ?
283
+ Tagged<Tag, Variants[Tag]>
284
+ : // Special syntax for recursive ADTs
285
+ [
286
+ Exclude<keyof Variants[Tag], "__labels">,
287
+ IsNever<Exclude<keyof Variants[Tag], "__labels">>,
288
+ ] extends [number, false] ?
289
+ Tagged<Tag, Variants[Tag]>
290
+ : // If the object is `{}`, we still treat it as `[]`
291
+ IsNever<keyof Variants[Tag]> extends true ? Tagged<Tag, []>
292
+ : // Special syntax for ADTs with only one object argument
293
+ Tagged<Tag, [fields: Variants[Tag]]>;
294
+ }>;
295
+
296
+ /**
297
+ * A constructor for an ADT.
298
+ */
299
+ export type Constructor<
300
+ Type extends Tagged | TypeLambda<never, Tagged>,
301
+ Tag extends string,
302
+ > =
303
+ [Type] extends (
304
+ [Tagged] // Non-generic ADT
305
+ ) ?
306
+ { readonly _tag: Tag } & ((
307
+ ...args: ExtractFields<Extract<Type, Tagged<Tag>>>
308
+ ) => Type)
309
+ : // Generic ADT
310
+ Type extends TypeLambda<[never], unknown> ?
311
+ unknown extends _UpperBound<HKTParams<Type>[0]> ?
312
+ { readonly _tag: Tag } & (<T = never>(
313
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T]>, Tag>>
314
+ ) => ApplyHKT<Type, [T]>)
315
+ : { readonly _tag: Tag } & (<
316
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
317
+ >(
318
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T]>, Tag>>
319
+ ) => ApplyHKT<Type, [T]>)
320
+ : Type extends TypeLambda<[never, never], unknown> ?
321
+ [unknown, unknown] extends (
322
+ [_UpperBound<HKTParams<Type>[0]>, _UpperBound<HKTParams<Type>[1]>]
323
+ ) ?
324
+ { readonly _tag: Tag } & (<T = never, U = never>(
325
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U]>, Tag>>
326
+ ) => ApplyHKT<Type, [T, U]>)
327
+ : unknown extends _UpperBound<HKTParams<Type>[0]> ?
328
+ { readonly _tag: Tag } & (<
329
+ T = never,
330
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
331
+ >(
332
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U]>, Tag>>
333
+ ) => ApplyHKT<Type, [T, U]>)
334
+ : unknown extends _UpperBound<HKTParams<Type>[1]> ?
335
+ { readonly _tag: Tag } & (<
336
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
337
+ U = never,
338
+ >(
339
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U]>, Tag>>
340
+ ) => ApplyHKT<Type, [T, U]>)
341
+ : { readonly _tag: Tag } & (<
342
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
343
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
344
+ >(
345
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U]>, Tag>>
346
+ ) => ApplyHKT<Type, [T, U]>)
347
+ : Type extends TypeLambda<[never, never, never], unknown> ?
348
+ { readonly _tag: Tag } & (<
349
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
350
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
351
+ V extends _UpperBound<HKTParams<Type>[2]> = never,
352
+ >(
353
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U, V]>, Tag>>
354
+ ) => ApplyHKT<Type, [T, U, V]>)
355
+ : Type extends TypeLambda<[never, never, never, never], unknown> ?
356
+ { readonly _tag: Tag } & (<
357
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
358
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
359
+ V extends _UpperBound<HKTParams<Type>[2]> = never,
360
+ W extends _UpperBound<HKTParams<Type>[3]> = never,
361
+ >(
362
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U, V, W]>, Tag>>
363
+ ) => ApplyHKT<Type, [T, U, V, W]>)
364
+ : Type extends TypeLambda<[never, never, never, never, never], unknown> ?
365
+ { readonly _tag: Tag } & (<
366
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
367
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
368
+ V extends _UpperBound<HKTParams<Type>[2]> = never,
369
+ W extends _UpperBound<HKTParams<Type>[3]> = never,
370
+ X extends _UpperBound<HKTParams<Type>[4]> = never,
371
+ >(
372
+ ...args: ExtractFields<FilterTagged<ApplyHKT<Type, [T, U, V, W, X]>, Tag>>
373
+ ) => ApplyHKT<Type, [T, U, V, W, X]>)
374
+ : Type extends (
375
+ TypeLambda<[never, never, never, never, never, never], unknown>
376
+ ) ?
377
+ { readonly _tag: Tag } & (<
378
+ T extends _UpperBound<HKTParams<Type>[0]> = never,
379
+ U extends _UpperBound<HKTParams<Type>[1]> = never,
380
+ V extends _UpperBound<HKTParams<Type>[2]> = never,
381
+ W extends _UpperBound<HKTParams<Type>[3]> = never,
382
+ X extends _UpperBound<HKTParams<Type>[4]> = never,
383
+ Y extends _UpperBound<HKTParams<Type>[5]> = never,
384
+ >(
385
+ ...args: ExtractFields<
386
+ FilterTagged<ApplyHKT<Type, [T, U, V, W, X, Y]>, Tag>
387
+ >
388
+ ) => ApplyHKT<Type, [T, U, V, W, X, Y]>)
389
+ : /* support up to 6 type parameters */ never;
390
+
391
+ /**
392
+ * A type guard function for an ADT.
393
+ */
394
+ export type Guard<T extends Tagged, Tag extends string> = (
395
+ adt: T,
396
+ ) => adt is FilterTagged<T, Tag>;
397
+
398
+ /**
399
+ * A deconstructor for an ADT.
400
+ */
401
+ export type Deconstructor<Type extends Tagged> = <T extends Type>(
402
+ adt: T,
403
+ ) => ExtractFields<T>;
404
+
405
+ type ConditionalDeconstructorOf<
406
+ F extends Tagged | TypeLambda<never, Tagged>,
407
+ Tag extends string,
408
+ > =
409
+ [F] extends [Tagged] ?
410
+ <R1, R2 = void>(
411
+ adt: F,
412
+ onMatch: (...args: ExtractFields<FilterTagged<F, Tag>>) => R1,
413
+ otherwise?: (adt: Exclude<F, Tagged<Tag>>) => R2,
414
+ ) => R1 | R2
415
+ : [F] extends [TypeLambda<[never], Tagged>] ?
416
+ ConditionalDeconstructor1<F, Tag>
417
+ : [F] extends [TypeLambda<[never, never], Tagged>] ?
418
+ ConditionalDeconstructor2<F, Tag>
419
+ : ConditionalDeconstructor<Instantiate<F>, Tag>;
420
+
421
+ type ConditionalDeconstructor1<
422
+ F extends TypeLambda<[never], Tagged>,
423
+ Tag extends string,
424
+ > =
425
+ [unknown] extends HKTParams<F> ?
426
+ <T, R1, R2 = void>(
427
+ adt: ApplyHKT<F, [T]>,
428
+ onMatch: (
429
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
430
+ ) => R1,
431
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T]>, Tagged<Tag>>) => R2,
432
+ ) => R1 | R2
433
+ : <T extends HKTParams<F>[0], R1, R2 = void>(
434
+ adt: ApplyHKT<F, [T]>,
435
+ onMatch: (
436
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
437
+ ) => R1,
438
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T]>, Tagged<Tag>>) => R2,
439
+ ) => R1 | R2;
440
+
441
+ type ConditionalDeconstructor2<
442
+ F extends TypeLambda<[never, never], Tagged>,
443
+ Tag extends string,
444
+ > =
445
+ [unknown, unknown] extends HKTParams<F> ?
446
+ <T, U, R1, R2 = void>(
447
+ adt: ApplyHKT<F, [T, U]>,
448
+ onMatch: (
449
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
450
+ ) => R1,
451
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T, U]>, Tagged<Tag>>) => R2,
452
+ ) => R1 | R2
453
+ : unknown extends HKTParams<F>[0] ?
454
+ <T, U extends HKTParams<F>[1], R1, R2 = void>(
455
+ adt: ApplyHKT<F, [T, U]>,
456
+ onMatch: (
457
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
458
+ ) => R1,
459
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T, U]>, Tagged<Tag>>) => R2,
460
+ ) => R1 | R2
461
+ : unknown extends HKTParams<F>[1] ?
462
+ <T extends HKTParams<F>[0], U, R1, R2 = void>(
463
+ adt: ApplyHKT<F, [T, U]>,
464
+ onMatch: (
465
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
466
+ ) => R1,
467
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T, U]>, Tagged<Tag>>) => R2,
468
+ ) => R1 | R2
469
+ : <T extends HKTParams<F>[0], U extends HKTParams<F>[1], R1, R2 = void>(
470
+ adt: ApplyHKT<F, [T, U]>,
471
+ onMatch: (
472
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
473
+ ) => R1,
474
+ otherwise?: (adt: Exclude<ApplyHKT<F, [T, U]>, Tagged<Tag>>) => R2,
475
+ ) => R1 | R2;
476
+
477
+ // Fallback conditional deconstructor type
478
+ /**
479
+ * A conditional {@link Deconstructor} for an ADT, which is similar to the `if let` syntax in Rust.
480
+ */
481
+ export type ConditionalDeconstructor<
482
+ Type extends Tagged,
483
+ Tag extends string,
484
+ > = <T extends Type, R1, R2 = void>(
485
+ adt: T,
486
+ onMatch: (...args: ExtractFields<FilterTagged<T, Tag>>) => R1,
487
+ otherwise?: (adt: Exclude<T, Tagged<Tag>>) => R2,
488
+ ) => R1 | R2;
489
+
490
+ type MatcherOf<F extends Tagged | TypeLambda<never, Tagged>> =
491
+ [F] extends [Tagged] ?
492
+ {
493
+ <R>(
494
+ adt: F,
495
+ cases: {
496
+ readonly [Tag in F["_tag"]]: (
497
+ ...args: ExtractFields<FilterTagged<F, Tag>>
498
+ ) => R;
499
+ },
500
+ ): R;
501
+ <R>(
502
+ adt: F,
503
+ cases: {
504
+ readonly [Tag in F["_tag"]]?: (
505
+ ...args: ExtractFields<FilterTagged<F, Tag>>
506
+ ) => R;
507
+ } & { _: (adt: F) => R },
508
+ ): R;
509
+ <R>(cases: {
510
+ readonly [Tag in F["_tag"]]: (
511
+ ...args: ExtractFields<FilterTagged<F, Tag>>
512
+ ) => R;
513
+ }): (adt: F) => R;
514
+ <R>(
515
+ cases: {
516
+ readonly [Tag in F["_tag"]]?: (
517
+ ...args: ExtractFields<FilterTagged<F, Tag>>
518
+ ) => R;
519
+ } & { _: (adt: F) => R },
520
+ ): (adt: F) => R;
521
+ }
522
+ : [F] extends [TypeLambda<[never], Tagged>] ? Matcher1<F>
523
+ : [F] extends [TypeLambda<[never, never], Tagged>] ? Matcher2<F>
524
+ : Matcher<Instantiate<F>>;
525
+
526
+ type Matcher1<F extends TypeLambda<[never], Tagged>> =
527
+ [unknown] extends HKTParams<F> ?
528
+ {
529
+ <T, R>(
530
+ adt: ApplyHKT<F, [T]>,
531
+ cases: {
532
+ readonly [Tag in Instantiate<F>["_tag"]]: (
533
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
534
+ ) => R;
535
+ },
536
+ ): R;
537
+ <T, R>(
538
+ adt: ApplyHKT<F, [T]>,
539
+ cases: {
540
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
541
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
542
+ ) => R;
543
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
544
+ ): R;
545
+ <T, R>(cases: {
546
+ readonly [Tag in Instantiate<F>["_tag"]]: (
547
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
548
+ ) => R;
549
+ }): (adt: ApplyHKT<F, [T]>) => R;
550
+ <T, R>(
551
+ cases: {
552
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
553
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
554
+ ) => R;
555
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
556
+ ): (adt: ApplyHKT<F, [T]>) => R;
557
+ }
558
+ : {
559
+ <T extends HKTParams<F>[0], R>(
560
+ adt: T,
561
+ cases: {
562
+ readonly [Tag in Instantiate<F>["_tag"]]: (
563
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
564
+ ) => R;
565
+ },
566
+ ): R;
567
+ <T extends HKTParams<F>[0], R>(
568
+ adt: ApplyHKT<F, [T]>,
569
+ cases: {
570
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
571
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
572
+ ) => R;
573
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
574
+ ): R;
575
+ <T extends HKTParams<F>[0], R>(cases: {
576
+ readonly [Tag in Instantiate<F>["_tag"]]: (
577
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
578
+ ) => R;
579
+ }): (adt: ApplyHKT<F, [T]>) => R;
580
+ <T extends HKTParams<F>[0], R>(
581
+ cases: {
582
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
583
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
584
+ ) => R;
585
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
586
+ ): (adt: ApplyHKT<F, [T]>) => R;
587
+ };
588
+
589
+ type Matcher2<F extends TypeLambda<[never, never], Tagged>> =
590
+ [unknown, unknown] extends HKTParams<F> ?
591
+ {
592
+ <T, U, R>(
593
+ adt: ApplyHKT<F, [T, U]>,
594
+ cases: {
595
+ readonly [Tag in Instantiate<F>["_tag"]]: (
596
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
597
+ ) => R;
598
+ },
599
+ ): R;
600
+ <T, U, R>(
601
+ adt: ApplyHKT<F, [T, U]>,
602
+ cases: {
603
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
604
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
605
+ ) => R;
606
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
607
+ ): R;
608
+ <T, U, R>(cases: {
609
+ readonly [Tag in Instantiate<F>["_tag"]]: (
610
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
611
+ ) => R;
612
+ }): (adt: ApplyHKT<F, [T, U]>) => R;
613
+ <T, U, R>(
614
+ cases: {
615
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
616
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
617
+ ) => R;
618
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
619
+ ): (adt: ApplyHKT<F, [T, U]>) => R;
620
+ }
621
+ : unknown extends HKTParams<F>[0] ?
622
+ {
623
+ <T, U extends HKTParams<F>[1], R>(
624
+ adt: ApplyHKT<F, [T, U]>,
625
+ cases: {
626
+ readonly [Tag in Instantiate<F>["_tag"]]: (
627
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
628
+ ) => R;
629
+ },
630
+ ): R;
631
+ <T, U extends HKTParams<F>[1], R>(
632
+ adt: ApplyHKT<F, [T, U]>,
633
+ cases: {
634
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
635
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
636
+ ) => R;
637
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
638
+ ): R;
639
+ <T, U extends HKTParams<F>[1], R>(cases: {
640
+ readonly [Tag in Instantiate<F>["_tag"]]: (
641
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
642
+ ) => R;
643
+ }): (adt: ApplyHKT<F, [T, U]>) => R;
644
+ <T, U extends HKTParams<F>[1], R>(
645
+ cases: {
646
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
647
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
648
+ ) => R;
649
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
650
+ ): (adt: ApplyHKT<F, [T, U]>) => R;
651
+ }
652
+ : unknown extends HKTParams<F>[1] ?
653
+ {
654
+ <T extends HKTParams<F>[0], U, R>(
655
+ adt: ApplyHKT<F, [T, U]>,
656
+ cases: {
657
+ readonly [Tag in Instantiate<F>["_tag"]]: (
658
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
659
+ ) => R;
660
+ },
661
+ ): R;
662
+ <T extends HKTParams<F>[0], U, R>(
663
+ adt: ApplyHKT<F, [T, U]>,
664
+ cases: {
665
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
666
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
667
+ ) => R;
668
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
669
+ ): R;
670
+ <T extends HKTParams<F>[0], U, R>(cases: {
671
+ readonly [Tag in Instantiate<F>["_tag"]]: (
672
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
673
+ ) => R;
674
+ }): (adt: ApplyHKT<F, [T, U]>) => R;
675
+ <T extends HKTParams<F>[0], U, R>(
676
+ cases: {
677
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
678
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
679
+ ) => R;
680
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
681
+ ): (adt: ApplyHKT<F, [T, U]>) => R;
682
+ }
683
+ : {
684
+ <T extends HKTParams<F>[0], U extends HKTParams<F>[1], R>(
685
+ adt: ApplyHKT<F, [T, U]>,
686
+ cases: {
687
+ readonly [Tag in Instantiate<F>["_tag"]]: (
688
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
689
+ ) => R;
690
+ },
691
+ ): R;
692
+ <T extends HKTParams<F>[0], U extends HKTParams<F>[1], R>(
693
+ adt: ApplyHKT<F, [T, U]>,
694
+ cases: {
695
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
696
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
697
+ ) => R;
698
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
699
+ ): R;
700
+ <T extends HKTParams<F>[0], U extends HKTParams<F>[1], R>(cases: {
701
+ readonly [Tag in Instantiate<F>["_tag"]]: (
702
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
703
+ ) => R;
704
+ }): (adt: ApplyHKT<F, [T, U]>) => R;
705
+ <T extends HKTParams<F>[0], U extends HKTParams<F>[1], R>(
706
+ cases: {
707
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
708
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
709
+ ) => R;
710
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => R },
711
+ ): (adt: ApplyHKT<F, [T, U]>) => R;
712
+ };
713
+
714
+ // Fallback matcher type
715
+ /**
716
+ * A {@linkcode match} function for a specific ADT.
717
+ */
718
+ export interface Matcher<Type extends Tagged> {
719
+ <T extends Type, R>(
720
+ adt: T,
721
+ cases: {
722
+ readonly [Tag in Type["_tag"]]: (
723
+ ...args: ExtractFields<FilterTagged<T, Tag>>
724
+ ) => R;
725
+ },
726
+ ): R;
727
+ <T extends Type, R>(
728
+ adt: T,
729
+ cases: {
730
+ readonly [Tag in Type["_tag"]]?: (
731
+ ...args: ExtractFields<FilterTagged<T, Tag>>
732
+ ) => R;
733
+ } & { _: (adt: T) => R },
734
+ ): R;
735
+ <T extends Type, R>(cases: {
736
+ readonly [Tag in Type["_tag"]]: (
737
+ ...args: ExtractFields<FilterTagged<T, Tag>>
738
+ ) => R;
739
+ }): (adt: T) => R;
740
+ <T extends Type, R>(
741
+ cases: {
742
+ readonly [Tag in Type["_tag"]]?: (
743
+ ...args: ExtractFields<FilterTagged<T, Tag>>
744
+ ) => R;
745
+ } & { _: (adt: T) => R },
746
+ ): (adt: T) => R;
747
+ }
748
+
749
+ type MatcherOfW<F extends Tagged | TypeLambda<never, Tagged>> =
750
+ [F] extends [Tagged] ?
751
+ {
752
+ <
753
+ Cases extends {
754
+ readonly [Tag in F["_tag"]]: (
755
+ ...args: ExtractFields<FilterTagged<F, Tag>>
756
+ ) => unknown;
757
+ },
758
+ >(
759
+ adt: F,
760
+ cases: Cases,
761
+ ): ReturnType<ValueOf<Cases>>;
762
+ <
763
+ Cases extends {
764
+ readonly [Tag in F["_tag"]]?: (
765
+ ...args: ExtractFields<FilterTagged<F, Tag>>
766
+ ) => unknown;
767
+ } & { _: (adt: F) => unknown },
768
+ >(
769
+ adt: F,
770
+ cases: Cases,
771
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
772
+ <
773
+ Cases extends {
774
+ readonly [Tag in F["_tag"]]: (
775
+ ...args: ExtractFields<FilterTagged<F, Tag>>
776
+ ) => unknown;
777
+ },
778
+ >(
779
+ cases: Cases,
780
+ ): (adt: F) => ReturnType<ValueOf<Cases>>;
781
+ <
782
+ Cases extends {
783
+ readonly [Tag in F["_tag"]]?: (
784
+ ...args: ExtractFields<FilterTagged<F, Tag>>
785
+ ) => unknown;
786
+ } & { _: (adt: F) => unknown },
787
+ >(
788
+ cases: Cases,
789
+ ): (
790
+ adt: F,
791
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
792
+ }
793
+ : [F] extends [TypeLambda<[never], Tagged>] ? Matcher1W<F>
794
+ : [F] extends [TypeLambda<[never, never], Tagged>] ? Matcher2W<F>
795
+ : MatcherW<Instantiate<F>>;
796
+
797
+ type Matcher1W<F extends TypeLambda<[never], Tagged>> =
798
+ [unknown] extends HKTParams<F> ?
799
+ {
800
+ <
801
+ T,
802
+ R,
803
+ Cases extends {
804
+ readonly [Tag in Instantiate<F>["_tag"]]: (
805
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
806
+ ) => unknown;
807
+ },
808
+ >(
809
+ adt: ApplyHKT<F, [T]>,
810
+ cases: Cases,
811
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
812
+ <
813
+ T,
814
+ R,
815
+ Cases extends {
816
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
817
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
818
+ ) => R;
819
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
820
+ >(
821
+ adt: ApplyHKT<F, [T]>,
822
+ cases: Cases,
823
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
824
+ <
825
+ T,
826
+ R,
827
+ Cases extends {
828
+ readonly [Tag in Instantiate<F>["_tag"]]: (
829
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
830
+ ) => unknown;
831
+ },
832
+ >(
833
+ cases: Cases,
834
+ ): (
835
+ adt: ApplyHKT<F, [T]>,
836
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
837
+ <
838
+ T,
839
+ R,
840
+ Cases extends {
841
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
842
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
843
+ ) => R;
844
+ } & { _: (adt: ApplyHKT<F, [T]>) => R },
845
+ >(
846
+ cases: Cases,
847
+ ): (
848
+ adt: ApplyHKT<F, [T]>,
849
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
850
+ }
851
+ : {
852
+ <
853
+ T extends HKTParams<F>[0],
854
+ Cases extends {
855
+ readonly [Tag in Instantiate<F>["_tag"]]: (
856
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
857
+ ) => unknown;
858
+ },
859
+ >(
860
+ adt: T,
861
+ cases: Cases,
862
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
863
+ <
864
+ T extends HKTParams<F>[0],
865
+ Cases extends {
866
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
867
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
868
+ ) => unknown;
869
+ } & { _: (adt: ApplyHKT<F, [T]>) => unknown },
870
+ >(
871
+ adt: ApplyHKT<F, [T]>,
872
+ cases: Cases,
873
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
874
+ <
875
+ T extends HKTParams<F>[0],
876
+ Cases extends {
877
+ readonly [Tag in Instantiate<F>["_tag"]]: (
878
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
879
+ ) => unknown;
880
+ },
881
+ >(
882
+ cases: Cases,
883
+ ): (
884
+ adt: ApplyHKT<F, [T]>,
885
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
886
+ <
887
+ T extends HKTParams<F>[0],
888
+ Cases extends {
889
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
890
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T]>, Tag>>
891
+ ) => unknown;
892
+ } & { _: (adt: ApplyHKT<F, [T]>) => unknown },
893
+ >(
894
+ cases: Cases,
895
+ ): (
896
+ adt: ApplyHKT<F, [T]>,
897
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
898
+ };
899
+
900
+ type Matcher2W<F extends TypeLambda<[never, never], Tagged>> =
901
+ [unknown, unknown] extends HKTParams<F> ?
902
+ {
903
+ <
904
+ T,
905
+ U,
906
+ Cases extends {
907
+ readonly [Tag in Instantiate<F>["_tag"]]: (
908
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
909
+ ) => unknown;
910
+ },
911
+ >(
912
+ adt: ApplyHKT<F, [T, U]>,
913
+ cases: Cases,
914
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
915
+ <
916
+ T,
917
+ U,
918
+ Cases extends {
919
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
920
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
921
+ ) => unknown;
922
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
923
+ >(
924
+ adt: ApplyHKT<F, [T, U]>,
925
+ cases: Cases,
926
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
927
+ <
928
+ T,
929
+ U,
930
+ Cases extends {
931
+ readonly [Tag in Instantiate<F>["_tag"]]: (
932
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
933
+ ) => unknown;
934
+ },
935
+ >(
936
+ cases: Cases,
937
+ ): (
938
+ adt: ApplyHKT<F, [T, U]>,
939
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
940
+ <
941
+ T,
942
+ U,
943
+ Cases extends {
944
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
945
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
946
+ ) => unknown;
947
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
948
+ >(
949
+ cases: Cases,
950
+ ): (
951
+ adt: ApplyHKT<F, [T, U]>,
952
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
953
+ }
954
+ : unknown extends HKTParams<F>[0] ?
955
+ {
956
+ <
957
+ T,
958
+ U extends HKTParams<F>[1],
959
+ Cases extends {
960
+ readonly [Tag in Instantiate<F>["_tag"]]: (
961
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
962
+ ) => unknown;
963
+ },
964
+ >(
965
+ adt: ApplyHKT<F, [T, U]>,
966
+ cases: Cases,
967
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
968
+ <
969
+ T,
970
+ U extends HKTParams<F>[1],
971
+ Cases extends {
972
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
973
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
974
+ ) => unknown;
975
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
976
+ >(
977
+ adt: ApplyHKT<F, [T, U]>,
978
+ cases: Cases,
979
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
980
+ <
981
+ T,
982
+ U extends HKTParams<F>[1],
983
+ Cases extends {
984
+ readonly [Tag in Instantiate<F>["_tag"]]: (
985
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
986
+ ) => unknown;
987
+ },
988
+ >(
989
+ cases: Cases,
990
+ ): (
991
+ adt: ApplyHKT<F, [T, U]>,
992
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
993
+ <
994
+ T,
995
+ U extends HKTParams<F>[1],
996
+ Cases extends {
997
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
998
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
999
+ ) => unknown;
1000
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
1001
+ >(
1002
+ cases: Cases,
1003
+ ): (
1004
+ adt: ApplyHKT<F, [T, U]>,
1005
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1006
+ }
1007
+ : unknown extends HKTParams<F>[1] ?
1008
+ {
1009
+ <
1010
+ T extends HKTParams<F>[0],
1011
+ U,
1012
+ Cases extends {
1013
+ readonly [Tag in Instantiate<F>["_tag"]]: (
1014
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1015
+ ) => unknown;
1016
+ },
1017
+ >(
1018
+ adt: ApplyHKT<F, [T, U]>,
1019
+ cases: Cases,
1020
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1021
+ <
1022
+ T extends HKTParams<F>[0],
1023
+ U,
1024
+ Cases extends {
1025
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
1026
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1027
+ ) => unknown;
1028
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
1029
+ >(
1030
+ adt: ApplyHKT<F, [T, U]>,
1031
+ cases: Cases,
1032
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1033
+ <
1034
+ T extends HKTParams<F>[0],
1035
+ U,
1036
+ Cases extends {
1037
+ readonly [Tag in Instantiate<F>["_tag"]]: (
1038
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1039
+ ) => unknown;
1040
+ },
1041
+ >(
1042
+ cases: Cases,
1043
+ ): (
1044
+ adt: ApplyHKT<F, [T, U]>,
1045
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1046
+ <
1047
+ T extends HKTParams<F>[0],
1048
+ U,
1049
+ Cases extends {
1050
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
1051
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1052
+ ) => unknown;
1053
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
1054
+ >(
1055
+ cases: Cases,
1056
+ ): (
1057
+ adt: ApplyHKT<F, [T, U]>,
1058
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1059
+ }
1060
+ : {
1061
+ <
1062
+ T extends HKTParams<F>[0],
1063
+ U extends HKTParams<F>[1],
1064
+ Cases extends {
1065
+ readonly [Tag in Instantiate<F>["_tag"]]: (
1066
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1067
+ ) => unknown;
1068
+ },
1069
+ >(
1070
+ adt: ApplyHKT<F, [T, U]>,
1071
+ cases: Cases,
1072
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1073
+ <
1074
+ T extends HKTParams<F>[0],
1075
+ U extends HKTParams<F>[1],
1076
+ Cases extends {
1077
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
1078
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1079
+ ) => unknown;
1080
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
1081
+ >(
1082
+ adt: ApplyHKT<F, [T, U]>,
1083
+ cases: Cases,
1084
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1085
+ <
1086
+ T extends HKTParams<F>[0],
1087
+ U extends HKTParams<F>[1],
1088
+ Cases extends {
1089
+ readonly [Tag in Instantiate<F>["_tag"]]: (
1090
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1091
+ ) => unknown;
1092
+ },
1093
+ >(
1094
+ cases: Cases,
1095
+ ): (
1096
+ adt: ApplyHKT<F, [T, U]>,
1097
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1098
+ <
1099
+ T extends HKTParams<F>[0],
1100
+ U extends HKTParams<F>[1],
1101
+ Cases extends {
1102
+ readonly [Tag in Instantiate<F>["_tag"]]?: (
1103
+ ...args: ExtractFields<FilterTagged<ApplyHKT<F, [T, U]>, Tag>>
1104
+ ) => unknown;
1105
+ } & { _: (adt: ApplyHKT<F, [T, U]>) => unknown },
1106
+ >(
1107
+ cases: Cases,
1108
+ ): (
1109
+ adt: ApplyHKT<F, [T, U]>,
1110
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1111
+ };
1112
+
1113
+ // Fallback matcher type
1114
+ /**
1115
+ * A {@linkcode matchW} function for a specific ADT.
1116
+ */
1117
+ export interface MatcherW<Type extends Tagged> {
1118
+ <
1119
+ T extends Type,
1120
+ Cases extends {
1121
+ readonly [Tag in Type["_tag"]]: (
1122
+ ...args: ExtractFields<FilterTagged<T, Tag>>
1123
+ ) => unknown;
1124
+ },
1125
+ >(
1126
+ adt: T,
1127
+ cases: Cases,
1128
+ ): ReturnType<ValueOf<Cases>>;
1129
+ <
1130
+ T extends Type,
1131
+ Cases extends {
1132
+ readonly [Tag in Type["_tag"]]?: (
1133
+ ...args: ExtractFields<FilterTagged<T, Tag>>
1134
+ ) => unknown;
1135
+ } & { _: (adt: T) => unknown },
1136
+ >(
1137
+ adt: T,
1138
+ cases: Cases,
1139
+ ): ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1140
+ <
1141
+ T extends Type,
1142
+ Cases extends {
1143
+ readonly [Tag in Type["_tag"]]: (
1144
+ ...args: ExtractFields<FilterTagged<T, Tag>>
1145
+ ) => unknown;
1146
+ },
1147
+ >(
1148
+ cases: Cases,
1149
+ ): (adt: T) => ReturnType<ValueOf<Cases>>;
1150
+ <
1151
+ T extends Type,
1152
+ Cases extends {
1153
+ readonly [Tag in Type["_tag"]]?: (
1154
+ ...args: ExtractFields<FilterTagged<T, Tag>>
1155
+ ) => unknown;
1156
+ } & { _: (adt: T) => unknown },
1157
+ >(
1158
+ cases: Cases,
1159
+ ): (
1160
+ adt: T,
1161
+ ) => ReturnType<Extract<ValueOf<Cases>, (...args: never) => unknown>>;
1162
+ }
1163
+
1164
+ /*****************
1165
+ * Utility types *
1166
+ *****************/
1167
+ /**
1168
+ * Instantiate a possibly higher-kinded {@linkcode Tagged}.
1169
+ * @private
1170
+ */
1171
+ type Instantiate<F extends Tagged | TypeLambda<never, Tagged>> =
1172
+ (F extends TypeLambda ? TolerantResult<F> : F) extends (
1173
+ infer R extends Tagged
1174
+ ) ?
1175
+ R
1176
+ : never;
1177
+
1178
+ /**
1179
+ * {@link Extract} a {@linkcode Tagged} from a union of {@linkcode Tagged}s.
1180
+ */
1181
+ type FilterTagged<T, Tag extends string> = Extract<T, { readonly _tag: Tag }>;
1182
+
1183
+ /**
1184
+ * Extract the fields of a {@linkcode Tagged} as a (possibly) labeled tuple.
1185
+ */
1186
+ type ExtractFields<F> = AddLabels<
1187
+ _ExtractFields<F>,
1188
+ F extends (
1189
+ Tagged<
1190
+ string,
1191
+ | (infer Labels extends readonly unknown[])
1192
+ | { readonly __labels: infer Labels extends readonly unknown[] }
1193
+ >
1194
+ ) ?
1195
+ Labels
1196
+ : []
1197
+ >;
1198
+ type _ExtractFields<
1199
+ F,
1200
+ Counter extends void[] = [],
1201
+ Acc extends unknown[] = [],
1202
+ > =
1203
+ F extends { readonly [K in `_${Counter["length"]}`]: infer T } ?
1204
+ _ExtractFields<F, [...Counter, void], [...Acc, T]>
1205
+ : Acc;
1206
+
1207
+ /************************
1208
+ * Common utility types *
1209
+ ************************/
1210
+ /**
1211
+ * Get the value type of an object type.
1212
+ * @private
1213
+ */
1214
+ type ValueOf<T> = T[keyof T];
1215
+
1216
+ /**
1217
+ * Check if a type is `never`.
1218
+ * @private
1219
+ */
1220
+ type IsNever<T> = [T] extends [never] ? true : false;
1221
+
1222
+ /**
1223
+ * Check if a type is `any`.
1224
+ *
1225
+ * Copied from [type-fest](https://github.com/sindresorhus/type-fest/blob/475a737e3223d7bd8e950f36998a17d520a93e4b/source/is-any.d.ts#L33).
1226
+ * @private
1227
+ */
1228
+ type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false;
1229
+
1230
+ /**
1231
+ * Convert a `string` type to a `number` type.
1232
+ * @private
1233
+ */
1234
+ type StringToNumber<S> = S extends `${infer N extends number}` ? N : never;
1235
+
1236
+ /**
1237
+ * Get numeric index of an object or a tuple type.
1238
+ * @private
1239
+ */
1240
+ type IndexOf<T, Counter extends void[] = [], Acc extends number = never> =
1241
+ T extends readonly unknown[] ?
1242
+ Counter["length"] extends T["length"] ?
1243
+ Acc & keyof T
1244
+ : IndexOf<T, [...Counter, void], Acc | Counter["length"]>
1245
+ : keyof T & number;
1246
+
1247
+ /**
1248
+ * Simply merge two object types and t. Optional keys are not considered.
1249
+ * @private
1250
+ */
1251
+ // NOTE: DO NOT add `extends infer R ? { [K in keyof R]: R[K] } : never` after the type definition
1252
+ // (which is a common hacking to make type information more readable), since it will cause
1253
+ // TypeScript to deeply evaluate the type, which we’ll lose tuple labels in the process.
1254
+ type Merge<L, R> = {
1255
+ // This `readonly` modifier amazingly makes TS to infer a more readable type in some cases,
1256
+ // e.g., `function safeDiv(n: number, d: number) { return d === 0 ? none : some(n / d); }`
1257
+ // will infer `Option<number>` instead of `Constructor<OptionHKT, "None"> | Option<number>`.
1258
+ // I don’t understand why it works, but it works.
1259
+ readonly [K in keyof L | keyof R]: K extends keyof L ? L[K]
1260
+ : K extends keyof R ? R[K]
1261
+ : never;
1262
+ };
1263
+
1264
+ /**
1265
+ * {@link Merge} multiple object types. Optional keys are not considered.
1266
+ * @private
1267
+ */
1268
+ type Spread<A, B, C = {}, D = {}, E = {}, F = {}, G = {}, H = {}> =
1269
+ Merge<
1270
+ A,
1271
+ Merge<B, Merge<C, Merge<D, Merge<E, Merge<F, Merge<G, H>>>>>>
1272
+ > extends infer R ?
1273
+ { [K in keyof R]: R[K] }
1274
+ : never;
1275
+
1276
+ /**
1277
+ * Add labels to a tuple type.
1278
+ * @private
1279
+ *
1280
+ * @example
1281
+ * ```typescript
1282
+ * type _1 = AddLabels<[1, 2, 3], [a: void, b: void, c: void]>; // => [a: 1, b: 2, c: 3]
1283
+ * type _2 = AddLabels<[1, 2, 3], [a: void, b: void]>; // => [a: 1, b: 2, 3]
1284
+ * type _3 = AddLabels<[1, 2], [a: void, b: void, c: void]>; // => [a: 1, b: 2]
1285
+ * ```
1286
+ */
1287
+ type AddLabels<
1288
+ TS extends unknown[],
1289
+ Labels extends readonly unknown[],
1290
+ Acc extends unknown[] = [],
1291
+ > =
1292
+ Labels extends [unknown, ...infer RestLabels] ?
1293
+ TS extends [infer Head, ...infer Tail] ?
1294
+ AddLabels<
1295
+ Tail,
1296
+ RestLabels,
1297
+ [...Acc, ...WrapLabel<Head, HeadPart<Labels>>]
1298
+ >
1299
+ : Acc
1300
+ : [...Acc, ...TS];
1301
+ type WrapLabel<T, Label> =
1302
+ { [K in keyof Label]: T } extends infer Part extends readonly [unknown] ? Part
1303
+ : never;
1304
+
1305
+ /**
1306
+ * Remove the first element from a tuple type (label is preserved).
1307
+ * @private
1308
+ *
1309
+ * @example
1310
+ * ```typescript
1311
+ * type _1 = Tail<[1, 2, 3]>; // => [2, 3]
1312
+ * type _2 = Tail<[1]>; // => []
1313
+ * type _3 = Tail<[]>; // => []
1314
+ * type _4 = Tail<[a: 1, b: 2, c: 3]>; // => [b: 2, c: 3]
1315
+ * ```
1316
+ */
1317
+ type Tail<TS> =
1318
+ TS extends [unknown, ...infer Tail] ? Tail
1319
+ : TS extends readonly [unknown, ...infer Tail] ? readonly [...Tail]
1320
+ : TS extends [] ? []
1321
+ : TS extends readonly [] ? readonly []
1322
+ : never;
1323
+ /**
1324
+ * Get the initial elements of a tuple type (label is preserved).
1325
+ * @private
1326
+ *
1327
+ * @example
1328
+ * ```typescript
1329
+ * type _1 = Init<[1, 2, 3]>; // => [1, 2]
1330
+ * type _2 = Init<[1]>; // => []
1331
+ * type _3 = Init<[]>; // => []
1332
+ * type _4 = Init<[a: 1, b: 2, c: 3]>; // => [a: 1, b: 2]
1333
+ * ```
1334
+ */
1335
+ type Init<TS> =
1336
+ TS extends [...infer Init, unknown] ? Init
1337
+ : TS extends readonly [...infer Init, unknown] ? readonly [...Init]
1338
+ : TS extends [] ? []
1339
+ : TS extends readonly [] ? readonly []
1340
+ : never;
1341
+ /**
1342
+ * Get the head part of a tuple type (label is preserved).
1343
+ * @private
1344
+ *
1345
+ * @example
1346
+ * ```typescript
1347
+ * type _1 = HeadPart<[1, 2, 3]>; // => [1]
1348
+ * type _2 = HeadPart<[1]>; // => [1]
1349
+ * type _3 = HeadPart<[]>; // => []
1350
+ * type _4 = HeadPart<[a: 1, b: 2, c: 3]>; // => [a: 1]
1351
+ * ```
1352
+ */
1353
+ type HeadPart<TS, Result = TS> =
1354
+ Result extends readonly [] | readonly [unknown] ? Result
1355
+ : HeadPart<Tail<TS>, Init<Result>>;
1356
+
1357
+ /***************
1358
+ * HKT related *
1359
+ ***************/
1360
+ /**
1361
+ * An **HKT** (**H**igher-**K**inded **T**ype) compatible with the
1362
+ * [hkt-core](https://github.com/Snowflyt/hkt-core) V1 standard.
1363
+ */
1364
+ interface TypeLambda<in Params extends unknown[] = any, out RetType = any> {
1365
+ /**
1366
+ * Metadata of the {@linkcode TypeLambda}.
1367
+ */
1368
+ readonly "~hkt": TypeLambdaMeta;
1369
+
1370
+ /**
1371
+ * type-level signature of the {@linkcode TypeLambda}.
1372
+ */
1373
+ readonly signature: (...args: Params) => RetType;
1374
+ }
1375
+ /**
1376
+ * Metadata of a {@linkcode TypeLambda}.
1377
+ */
1378
+ interface TypeLambdaMeta {
1379
+ /**
1380
+ * The version number of the {@linkcode TypeLambda} specification.
1381
+ */
1382
+ readonly version: 1;
1383
+ }
1384
+
1385
+ /**
1386
+ * Apply an HKT with arguments.
1387
+ */
1388
+ type ApplyHKT<F, Args> =
1389
+ F & { readonly Args: (_: Args) => void } extends (
1390
+ infer F extends { readonly return: unknown }
1391
+ ) ?
1392
+ F["return"]
1393
+ : never;
1394
+
1395
+ /**
1396
+ * Get the declared parameter types of an HKT.
1397
+ */
1398
+ type HKTParams<F> =
1399
+ F extends { readonly signature: (...args: infer Params) => unknown } ? Params
1400
+ : never;
1401
+
1402
+ /**
1403
+ * Get a _tolerant_ result of an HKT.
1404
+ * @private
1405
+ *
1406
+ * @example
1407
+ * ```typescript
1408
+ * interface OptionHKT extends HKT {
1409
+ * return: Option<Arg0<this>>;
1410
+ * }
1411
+ *
1412
+ * type _1 = TolerantResult<OptionHKT>; // => Option<unknown>
1413
+ * ```
1414
+ */
1415
+ export type TolerantResult<F extends TypeLambda> =
1416
+ HKTParams<F> extends infer Params extends unknown[] ?
1417
+ ApplyHKT<
1418
+ F,
1419
+ {
1420
+ [K in keyof Params]: _TestParameterVarianceAtIndex<
1421
+ F,
1422
+ StringToNumber<K>
1423
+ > extends infer Variance ?
1424
+ Variance extends "invariant" ? any
1425
+ : Variance extends "contravariant" ? never
1426
+ : _UpperBound<Params[K]>
1427
+ : never;
1428
+ }
1429
+ >
1430
+ : never;
1431
+ // Infer the variance of each type parameter for a specific parameter or return type
1432
+ // The implementation refers to the description of variance inference in the Python typing
1433
+ // documentation: https://typing.readthedocs.io/en/latest/spec/generics.html#variance-inference
1434
+ type _TestParameterVarianceAtIndex<F extends TypeLambda, Index extends number> =
1435
+ HKTParams<F> extends infer Params extends unknown[] ?
1436
+ _CheckVariance<
1437
+ ApplyHKT<F, { [K in keyof Params]: never }>,
1438
+ ApplyHKT<
1439
+ F,
1440
+ {
1441
+ [K in keyof Params]: StringToNumber<K> extends Index ?
1442
+ _UpperBound<Params[K]>
1443
+ : never;
1444
+ }
1445
+ >
1446
+ >
1447
+ : never;
1448
+ type _UpperBound<T> = IsAny<T> extends true ? unknown : T;
1449
+ type _CheckVariance<Lower, Upper> =
1450
+ [Lower] extends [Upper] ?
1451
+ [Upper] extends [Lower] ?
1452
+ "irrelevant"
1453
+ : "covariant"
1454
+ : [Upper] extends [Lower] ? "contravariant"
1455
+ : "invariant";