@onrails/pattern 0.3.3 → 0.3.5

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/dist/index.d.ts CHANGED
@@ -1,329 +1,6 @@
1
- export { matchTag } from './tag.js';
2
-
3
- /**
4
- * Use in `default` branches (or after manual narrowing) to assert a union has
5
- * been fully handled. If every case is handled, `value` is `never` and the call
6
- * type-checks; an unhandled case makes `value` non-`never`, surfacing a compile
7
- * error. Always throws at runtime.
8
- *
9
- * @param value - the value that should be unreachable (typed `never`)
10
- * @param message - error message prefix; defaults to `"Unreachable"`
11
- * @throws always — the branch is meant to be unreachable
12
- *
13
- * @example
14
- * ```ts
15
- * type Shape = { kind: "circle"; r: number } | { kind: "square"; s: number };
16
- *
17
- * function area(shape: Shape): number {
18
- * switch (shape.kind) {
19
- * case "circle": return Math.PI * shape.r ** 2;
20
- * case "square": return shape.s ** 2;
21
- * default: return assertNever(shape); // type error if a case is added
22
- * }
23
- * }
24
- * ```
25
- */
26
- declare const assertNever: (value: never, message?: string) => never;
27
-
28
- type GuardTarget<F> = F extends (input: any) => input is infer U ? U : never;
29
- type NarrowObject<T, P> = [Extract<T, P>] extends [never] ? T & P : Extract<T, P>;
30
- /** Union of members narrowed by each pattern in `Ps` (multi-branch handlers). */
31
- type NarrowUnion<T, Ps extends readonly unknown[]> = Narrow<T, Ps[number]>;
32
- /** Narrows `input` when `pattern` is a shallow object, literal discriminant, or type predicate. */
33
- type Narrow<T, P> = P extends (input: T) => boolean ? [GuardTarget<P>] extends [never] ? T : NarrowObject<T, GuardTarget<P>> : P extends Record<string, unknown> ? NarrowObject<T, P> : T extends P ? Extract<T, P> : T;
34
-
35
- /**
36
- * Union members of `T` ruled out by pattern `P`.
37
- * Boolean guards (non-predicates) do not advance exhaustiveness — use `when(isX)` or `.otherwise()`.
38
- */
39
- type ExhaustMatched<T, P> = P extends (input: T) => boolean ? [GuardTarget<P>] extends [never] ? never : Extract<T, GuardTarget<P>> : Extract<T, P>;
40
- /** Union of cases already handled by prior branches. */
41
- type HandledUnion<Handled extends readonly unknown[]> = Handled[number];
42
- /** Input cases not yet covered by `.with` / `.withOneOf` / `.withEither`. */
43
- type RemainingCases<T, Handled extends readonly unknown[]> = Exclude<T, HandledUnion<Handled>>;
44
- /** `true` when every member of `T` appears in `Handled`, otherwise `false`. */
45
- type IsExhaustive<T, Handled extends readonly unknown[]> = [
46
- RemainingCases<T, Handled>
47
- ] extends [never] ? true : false;
48
- /** `.exhaustive()` shape: the value `R` when an input was given, else a `(input: T) => R` matcher. */
49
- type ExhaustiveOutput<R, HasInput extends boolean, T> = HasInput extends true ? R : (input: T) => R;
50
- /** Returned by `.exhaustive()` when union cases are still missing (compile-time error). */
51
- type NonExhaustiveError<Remaining> = {
52
- readonly __nonExhaustive: "Add .with() branches for remaining cases";
53
- readonly remaining: Remaining;
54
- };
55
- /** `.exhaustive()` return type — `NonExhaustiveError` when cases are missing. */
56
- type ExhaustiveResult<T, Handled extends readonly unknown[], R, HasInput extends boolean> = IsExhaustive<T, Handled> extends true ? ExhaustiveOutput<R, HasInput, T> : NonExhaustiveError<RemainingCases<T, Handled>>;
57
-
58
- /**
59
- * A pattern that matches a value of type `T`. One of:
60
- *
61
- * - **Literal** — a primitive (`"a"`, `42`, `true`) matched by `===`.
62
- * - **Object pattern** — a `Partial<T>` of shallow key/value pairs that
63
- * must all match by strict equality. Only available when `T` is an
64
- * object type.
65
- * - **Guard** — a function `(input: T) => boolean`. Type predicates
66
- * (`(x): x is U`) narrow the handler input via {@link Narrow}.
67
- *
68
- * @example
69
- * ```ts
70
- * type Event = { kind: "click"; x: number; y: number } | { kind: "key"; code: string };
71
- *
72
- * const p1: Pattern<Event> = { kind: "click" }; // object pattern
73
- * const p2: Pattern<Event> = (e) => e.kind === "key"; // guard
74
- * ```
75
- */
76
- type Pattern<T> = T | ObjectPattern<T> | PatternGuard<T>;
77
- type ObjectPattern<T> = T extends object ? Partial<T> : never;
78
- type PatternGuard<T> = (input: T) => boolean;
79
- type Case<T, R> = {
80
- readonly test: (input: T) => boolean;
81
- readonly run: (input: T) => R;
82
- };
83
- declare const NO_INPUT: unique symbol;
84
- type NoInput = typeof NO_INPUT;
85
- type NextResult<Locked extends boolean, R, R2> = Locked extends true ? R : R | R2;
86
- type HandlerReturn<Locked extends boolean, R, R2> = Locked extends true ? R : R2;
87
- /**
88
- * Fluent builder for {@link match}. Tracks the handled cases at the type
89
- * level so `.exhaustive()` only compiles when every union member is covered.
90
- *
91
- * The `Locked` phantom flag (set via `.returnType<R>()`) constrains every
92
- * subsequent handler to return `R`, useful when branch return-type
93
- * inference would widen to a union narrower than the target slot
94
- * (e.g. `ReactNode`, an API DTO).
95
- *
96
- * @example
97
- * ```ts
98
- * const describe = match<Event>()
99
- * .with({ kind: "click" }, (e) => `click @ ${e.x},${e.y}`)
100
- * .with({ kind: "key" }, (e) => `key ${e.code}`)
101
- * .exhaustive();
102
- * ```
103
- */
104
- declare class MatchBuilder<T, R = never, HasInput extends boolean = false, Handled extends readonly unknown[] = [], Locked extends boolean = false> {
105
- private readonly cases;
106
- private readonly input;
107
- /** Phantom tuple of handled cases — only its compile-time contents matter. */
108
- readonly _handled: Handled;
109
- /**
110
- * Construct a builder directly. Prefer the {@link match} factory — the
111
- * constructor exists so internal combinators can thread accumulated `cases`,
112
- * the optional captured `input`, and the phantom `_handled` tuple.
113
- */
114
- constructor(cases: readonly Case<T, unknown>[], input: T | NoInput,
115
- /** Phantom tuple of handled cases — only its compile-time contents matter. */
116
- _handled?: Handled);
117
- /** Appends a runtime case plus a phantom `H` entry on the handled tuple. */
118
- private addCase;
119
- /**
120
- * Add a case: when `input` satisfies `pattern`, run `handler` on the narrowed
121
- * value. The pattern may be a literal, a shallow object pattern, or a guard
122
- * ({@link Pattern}); a type-predicate guard narrows the handler input. Each
123
- * `.with` advances compile-time exhaustiveness tracking so `.exhaustive()`
124
- * knows which union members remain.
125
- *
126
- * @param pattern - literal, object pattern, or guard to test against
127
- * @param handler - run on the narrowed input when the pattern matches
128
- * @returns a builder with this case appended
129
- *
130
- * @example
131
- * ```ts
132
- * type Event =
133
- * | { type: "message"; content: string }
134
- * | { type: "error"; message: string }
135
- * | { type: "done" };
136
- *
137
- * const summary = match({ type: "message", content: "hi" } as Event)
138
- * .with({ type: "message" }, (e) => e.content) // e: { type: "message"; content: string }
139
- * .with({ type: "error" }, (e) => e.message)
140
- * .with({ type: "done" }, () => "—")
141
- * .exhaustive(); // "hi"
142
- * ```
143
- */
144
- with<const P extends Pattern<T>, R2>(pattern: P, handler: (input: Narrow<T, P>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, ExhaustMatched<T, P>], Locked>;
145
- /**
146
- * One handler for several patterns (OR). The handler input is the union of
147
- * the members narrowed by each pattern, and all of them are marked handled
148
- * for exhaustiveness.
149
- *
150
- * @param patterns - patterns to test; any match runs `handler`
151
- * @param handler - run on the union of narrowed members
152
- * @returns a builder with the shared case appended
153
- *
154
- * @example
155
- * ```ts
156
- * type Job =
157
- * | { kind: "queued"; id: string }
158
- * | { kind: "running"; id: string }
159
- * | { kind: "done"; ok: boolean };
160
- *
161
- * const status = match({ kind: "running", id: "j1" } as Job)
162
- * .withOneOf([{ kind: "queued" }, { kind: "running" }], (j) => j.id) // j: queued | running
163
- * .with({ kind: "done" }, (j) => (j.ok ? "yes" : "no"))
164
- * .exhaustive(); // "j1"
165
- * ```
166
- */
167
- withOneOf<const Ps extends readonly Pattern<T>[], R2>(patterns: Ps, handler: (input: NarrowUnion<T, Ps>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, NarrowUnion<T, Ps>], Locked>;
168
- /**
169
- * {@link withOneOf} for exactly two patterns — sugar that avoids the array.
170
- *
171
- * @param pattern1 - first pattern
172
- * @param pattern2 - second pattern
173
- * @param handler - run on the union of the two narrowed members
174
- * @returns a builder with the shared case appended
175
- *
176
- * @example
177
- * ```ts
178
- * type Provider = "ollama" | "openrouter";
179
- *
180
- * const pick = match<Provider>()
181
- * .withEither("ollama", "openrouter", (p) => p) // p: "ollama" | "openrouter"
182
- * .exhaustive();
183
- * pick("ollama"); // "ollama"
184
- * ```
185
- */
186
- withEither<const P1 extends Pattern<T>, const P2 extends Pattern<T>, R2>(pattern1: P1, pattern2: P2, handler: (input: NarrowUnion<T, readonly [P1, P2]>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, NarrowUnion<T, readonly [P1, P2]>], Locked>;
187
- /**
188
- * Lock the result type. All subsequent `.with()` handlers must return `R2`.
189
- * Useful when branch return-type inference widens to a union narrower than
190
- * the slot the match feeds into (e.g. `ReactNode`).
191
- *
192
- * Also the fix for literal-widening: when a handler returns an object whose
193
- * field is a union literal, inference widens it (e.g. `{ mode: "native" }`
194
- * becomes `{ mode: string }`, breaking a `Mode` union). Lock the target type
195
- * instead of sprinkling `as const`:
196
- *
197
- * @example
198
- * ```ts
199
- * type State = { mode: "compat" | "native" };
200
- * const next = match(flag)
201
- * .returnType<State>() // handlers now checked against State
202
- * .with("--native", () => ({ mode: "native" })) // no `as const` needed
203
- * .otherwise(() => state);
204
- * ```
205
- */
206
- returnType<R2>(): MatchBuilder<T, R2, HasInput, Handled, true>;
207
- /**
208
- * Run the accumulated cases now, without a compile-time exhaustiveness check.
209
- * Uses the value passed to {@link match} (data-first), or the value passed to
210
- * `run` (curried). Throws `"Non-exhaustive match"` if no case matches at
211
- * runtime — prefer {@link exhaustive} when the input is a closed union, or
212
- * {@link otherwise} for an explicit fallback.
213
- *
214
- * @param input - the value to match when the builder was created curried
215
- * @returns the matched handler's result
216
- * @throws if no case matches the input
217
- *
218
- * @example
219
- * ```ts
220
- * type Event = { type: "error"; message: string } | { type: "done" };
221
- *
222
- * const msg = match({ type: "error", message: "x" } as Event)
223
- * .with({ type: "error" }, (e) => e.message)
224
- * .run({ type: "error", message: "net" }); // "net"
225
- * ```
226
- */
227
- run(...input: readonly [input: T] | readonly []): R;
228
- /**
229
- * Settle the match with a compile-time exhaustiveness guarantee. Only
230
- * type-checks when every member of `T` has been handled; otherwise the return
231
- * type becomes {@link NonExhaustiveError}, surfacing the missing cases as a
232
- * type error. Returns the result directly when data-first ({@link match}
233
- * called with a value), or a reusable `(input: T) => R` matcher when curried.
234
- *
235
- * @returns the result (data-first) or a matcher function (curried)
236
- *
237
- * @example
238
- * ```ts
239
- * type Event =
240
- * | { type: "message"; content: string }
241
- * | { type: "error"; message: string }
242
- * | { type: "done" };
243
- *
244
- * // Curried — reusable matcher; omitting a `.with` would be a type error.
245
- * const describe = match<Event>()
246
- * .with({ type: "message" }, (e) => e.content)
247
- * .with({ type: "error" }, (e) => `! ${e.message}`)
248
- * .with({ type: "done" }, () => "done")
249
- * .exhaustive();
250
- * describe({ type: "done" }); // "done"
251
- * ```
252
- */
253
- exhaustive(): ExhaustiveResult<T, Handled, R, HasInput>;
254
- /**
255
- * Settle the match with a catch-all fallback, dropping the exhaustiveness
256
- * requirement — `handler` runs (on the full input type) when no case matched.
257
- * Returns the result directly when data-first, or a reusable matcher when
258
- * curried.
259
- *
260
- * @param handler - fallback run on the input when no prior case matched
261
- * @returns the result (data-first) or a matcher function (curried)
262
- *
263
- * @example
264
- * ```ts
265
- * type Event =
266
- * | { type: "message"; content: string }
267
- * | { type: "error"; message: string }
268
- * | { type: "done" };
269
- *
270
- * const len = match({ type: "done" } as Event)
271
- * .with({ type: "message" }, (e) => e.content.length)
272
- * .otherwise(() => -1); // -1
273
- * ```
274
- */
275
- otherwise(handler: (input: T) => R): HasInput extends true ? R : (input: T) => R;
276
- }
277
- /**
278
- * Start a pattern-matching expression. Two call shapes:
279
- *
280
- * - `match(value)` — data-first; subsequent `.exhaustive()` or
281
- * `.otherwise(fn)` runs immediately against `value`.
282
- * - `match<T>()` — curried; subsequent `.exhaustive()` returns a function
283
- * `(value: T) => R` for use in `pipe(...)` or as a reusable matcher.
284
- *
285
- * @example
286
- * ```ts
287
- * // Data-first
288
- * const out = match({ kind: "click", x: 1, y: 2 } as Event)
289
- * .with({ kind: "click" }, (e) => e.x + e.y)
290
- * .with({ kind: "key" }, () => -1)
291
- * .exhaustive();
292
- *
293
- * // Curried — build a reusable matcher
294
- * const describe = match<Event>()
295
- * .with({ kind: "click" }, (e) => `click @ ${e.x},${e.y}`)
296
- * .with({ kind: "key" }, (e) => `key ${e.code}`)
297
- * .exhaustive();
298
- * ```
299
- */
300
- declare function match<T>(input: T): MatchBuilder<T, never, true>;
301
- declare function match<T = never>(): MatchBuilder<T, never, false>;
302
-
303
- /**
304
- * Guard pattern for {@link match}.with.
305
- *
306
- * Accepts plain boolean guards (`(x) => x.length > 0`) or TS type-predicate
307
- * guards (`(x): x is Foo => ...`). For type predicates, the matched handler's
308
- * input is narrowed to the predicate's target type via {@link Narrow}.
309
- *
310
- * Note: a plain boolean guard does not advance exhaustiveness tracking — pair
311
- * it with `.otherwise()`, or use a type predicate to narrow the union.
312
- *
313
- * @param guard - boolean or type-predicate guard function
314
- * @returns the guard, usable as a {@link Pattern} in `.with`
315
- *
316
- * @example
317
- * ```ts
318
- * const sign = match<number>()
319
- * .with(when((n: number) => n < 0), () => "neg")
320
- * .with(when((n: number) => n >= 0), () => "non-neg")
321
- * .exhaustive();
322
- * sign(-1); // "neg"
323
- * sign(0); // "non-neg"
324
- * ```
325
- */
326
- declare function when<T, U extends T>(guard: (input: T) => input is U): (input: T) => input is U;
327
- declare function when<T>(guard: (input: T) => boolean): Pattern<T>;
328
-
329
- export { type ExhaustMatched, type ExhaustiveResult, type HandledUnion, type IsExhaustive, MatchBuilder, type Narrow, type NarrowUnion, type NonExhaustiveError, type Pattern, type RemainingCases, assertNever, match, when };
1
+ export { assertNever } from "./assert.js";
2
+ export type { ExhaustiveResult, ExhaustMatched, HandledUnion, IsExhaustive, NonExhaustiveError, RemainingCases, } from "./exhaustive.js";
3
+ export { MatchBuilder, match, type Pattern } from "./match.js";
4
+ export type { Narrow, NarrowUnion } from "./narrow.js";
5
+ export { matchTag } from "./tag.js";
6
+ export { when } from "./when.js";
@@ -0,0 +1,247 @@
1
+ import type { ExhaustiveResult, ExhaustMatched } from "./exhaustive.js";
2
+ import type { Narrow, NarrowUnion } from "./narrow.js";
3
+ /**
4
+ * A pattern that matches a value of type `T`. One of:
5
+ *
6
+ * - **Literal** — a primitive (`"a"`, `42`, `true`) matched by `===`.
7
+ * - **Object pattern** — a `Partial<T>` of shallow key/value pairs that
8
+ * must all match by strict equality. Only available when `T` is an
9
+ * object type.
10
+ * - **Guard** — a function `(input: T) => boolean`. Type predicates
11
+ * (`(x): x is U`) narrow the handler input via {@link Narrow}.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * type Event = { kind: "click"; x: number; y: number } | { kind: "key"; code: string };
16
+ *
17
+ * const p1: Pattern<Event> = { kind: "click" }; // object pattern
18
+ * const p2: Pattern<Event> = (e) => e.kind === "key"; // guard
19
+ * ```
20
+ */
21
+ export type Pattern<T> = T | ObjectPattern<T> | PatternGuard<T>;
22
+ type ObjectPattern<T> = T extends object ? Partial<T> : never;
23
+ type PatternGuard<T> = (input: T) => boolean;
24
+ type Case<T, R> = {
25
+ readonly test: (input: T) => boolean;
26
+ readonly run: (input: T) => R;
27
+ };
28
+ declare const NO_INPUT: unique symbol;
29
+ type NoInput = typeof NO_INPUT;
30
+ type NextResult<Locked extends boolean, R, R2> = Locked extends true ? R : R | R2;
31
+ type HandlerReturn<Locked extends boolean, R, R2> = Locked extends true ? R : R2;
32
+ /**
33
+ * Fluent builder for {@link match}. Tracks the handled cases at the type
34
+ * level so `.exhaustive()` only compiles when every union member is covered.
35
+ *
36
+ * The `Locked` phantom flag (set via `.returnType<R>()`) constrains every
37
+ * subsequent handler to return `R`, useful when branch return-type
38
+ * inference would widen to a union narrower than the target slot
39
+ * (e.g. `ReactNode`, an API DTO).
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const describe = match<Event>()
44
+ * .with({ kind: "click" }, (e) => `click @ ${e.x},${e.y}`)
45
+ * .with({ kind: "key" }, (e) => `key ${e.code}`)
46
+ * .exhaustive();
47
+ * ```
48
+ */
49
+ export declare class MatchBuilder<T, R = never, HasInput extends boolean = false, Handled extends readonly unknown[] = [], Locked extends boolean = false> {
50
+ private readonly cases;
51
+ private readonly input;
52
+ /** Phantom tuple of handled cases — only its compile-time contents matter. */
53
+ readonly _handled: Handled;
54
+ /**
55
+ * Construct a builder directly. Prefer the {@link match} factory — the
56
+ * constructor exists so internal combinators can thread accumulated `cases`,
57
+ * the optional captured `input`, and the phantom `_handled` tuple.
58
+ */
59
+ constructor(cases: readonly Case<T, unknown>[], input: T | NoInput,
60
+ /** Phantom tuple of handled cases — only its compile-time contents matter. */
61
+ _handled?: Handled);
62
+ /** Appends a runtime case plus a phantom `H` entry on the handled tuple. */
63
+ private addCase;
64
+ /**
65
+ * Add a case: when `input` satisfies `pattern`, run `handler` on the narrowed
66
+ * value. The pattern may be a literal, a shallow object pattern, or a guard
67
+ * ({@link Pattern}); a type-predicate guard narrows the handler input. Each
68
+ * `.with` advances compile-time exhaustiveness tracking so `.exhaustive()`
69
+ * knows which union members remain.
70
+ *
71
+ * @param pattern - literal, object pattern, or guard to test against
72
+ * @param handler - run on the narrowed input when the pattern matches
73
+ * @returns a builder with this case appended
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * type Event =
78
+ * | { type: "message"; content: string }
79
+ * | { type: "error"; message: string }
80
+ * | { type: "done" };
81
+ *
82
+ * const summary = match({ type: "message", content: "hi" } as Event)
83
+ * .with({ type: "message" }, (e) => e.content) // e: { type: "message"; content: string }
84
+ * .with({ type: "error" }, (e) => e.message)
85
+ * .with({ type: "done" }, () => "—")
86
+ * .exhaustive(); // "hi"
87
+ * ```
88
+ */
89
+ with<const P extends Pattern<T>, R2>(pattern: P, handler: (input: Narrow<T, P>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, ExhaustMatched<T, P>], Locked>;
90
+ /**
91
+ * One handler for several patterns (OR). The handler input is the union of
92
+ * the members narrowed by each pattern, and all of them are marked handled
93
+ * for exhaustiveness.
94
+ *
95
+ * @param patterns - patterns to test; any match runs `handler`
96
+ * @param handler - run on the union of narrowed members
97
+ * @returns a builder with the shared case appended
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * type Job =
102
+ * | { kind: "queued"; id: string }
103
+ * | { kind: "running"; id: string }
104
+ * | { kind: "done"; ok: boolean };
105
+ *
106
+ * const status = match({ kind: "running", id: "j1" } as Job)
107
+ * .withOneOf([{ kind: "queued" }, { kind: "running" }], (j) => j.id) // j: queued | running
108
+ * .with({ kind: "done" }, (j) => (j.ok ? "yes" : "no"))
109
+ * .exhaustive(); // "j1"
110
+ * ```
111
+ */
112
+ withOneOf<const Ps extends readonly Pattern<T>[], R2>(patterns: Ps, handler: (input: NarrowUnion<T, Ps>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, NarrowUnion<T, Ps>], Locked>;
113
+ /**
114
+ * {@link withOneOf} for exactly two patterns — sugar that avoids the array.
115
+ *
116
+ * @param pattern1 - first pattern
117
+ * @param pattern2 - second pattern
118
+ * @param handler - run on the union of the two narrowed members
119
+ * @returns a builder with the shared case appended
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * type Provider = "ollama" | "openrouter";
124
+ *
125
+ * const pick = match<Provider>()
126
+ * .withEither("ollama", "openrouter", (p) => p) // p: "ollama" | "openrouter"
127
+ * .exhaustive();
128
+ * pick("ollama"); // "ollama"
129
+ * ```
130
+ */
131
+ withEither<const P1 extends Pattern<T>, const P2 extends Pattern<T>, R2>(pattern1: P1, pattern2: P2, handler: (input: NarrowUnion<T, readonly [P1, P2]>) => HandlerReturn<Locked, R, R2>): MatchBuilder<T, NextResult<Locked, R, R2>, HasInput, readonly [...Handled, NarrowUnion<T, readonly [P1, P2]>], Locked>;
132
+ /**
133
+ * Lock the result type. All subsequent `.with()` handlers must return `R2`.
134
+ * Useful when branch return-type inference widens to a union narrower than
135
+ * the slot the match feeds into (e.g. `ReactNode`).
136
+ *
137
+ * Also the fix for literal-widening: when a handler returns an object whose
138
+ * field is a union literal, inference widens it (e.g. `{ mode: "native" }`
139
+ * becomes `{ mode: string }`, breaking a `Mode` union). Lock the target type
140
+ * instead of sprinkling `as const`:
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * type State = { mode: "compat" | "native" };
145
+ * const next = match(flag)
146
+ * .returnType<State>() // handlers now checked against State
147
+ * .with("--native", () => ({ mode: "native" })) // no `as const` needed
148
+ * .otherwise(() => state);
149
+ * ```
150
+ */
151
+ returnType<R2>(): MatchBuilder<T, R2, HasInput, Handled, true>;
152
+ /**
153
+ * Run the accumulated cases now, without a compile-time exhaustiveness check.
154
+ * Uses the value passed to {@link match} (data-first), or the value passed to
155
+ * `run` (curried). Throws `"Non-exhaustive match"` if no case matches at
156
+ * runtime — prefer {@link exhaustive} when the input is a closed union, or
157
+ * {@link otherwise} for an explicit fallback.
158
+ *
159
+ * @param input - the value to match when the builder was created curried
160
+ * @returns the matched handler's result
161
+ * @throws if no case matches the input
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * type Event = { type: "error"; message: string } | { type: "done" };
166
+ *
167
+ * const msg = match({ type: "error", message: "x" } as Event)
168
+ * .with({ type: "error" }, (e) => e.message)
169
+ * .run({ type: "error", message: "net" }); // "net"
170
+ * ```
171
+ */
172
+ run(...input: readonly [input: T] | readonly []): R;
173
+ /**
174
+ * Settle the match with a compile-time exhaustiveness guarantee. Only
175
+ * type-checks when every member of `T` has been handled; otherwise the return
176
+ * type becomes {@link NonExhaustiveError}, surfacing the missing cases as a
177
+ * type error. Returns the result directly when data-first ({@link match}
178
+ * called with a value), or a reusable `(input: T) => R` matcher when curried.
179
+ *
180
+ * @returns the result (data-first) or a matcher function (curried)
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * type Event =
185
+ * | { type: "message"; content: string }
186
+ * | { type: "error"; message: string }
187
+ * | { type: "done" };
188
+ *
189
+ * // Curried — reusable matcher; omitting a `.with` would be a type error.
190
+ * const describe = match<Event>()
191
+ * .with({ type: "message" }, (e) => e.content)
192
+ * .with({ type: "error" }, (e) => `! ${e.message}`)
193
+ * .with({ type: "done" }, () => "done")
194
+ * .exhaustive();
195
+ * describe({ type: "done" }); // "done"
196
+ * ```
197
+ */
198
+ exhaustive(): ExhaustiveResult<T, Handled, R, HasInput>;
199
+ /**
200
+ * Settle the match with a catch-all fallback, dropping the exhaustiveness
201
+ * requirement — `handler` runs (on the full input type) when no case matched.
202
+ * Returns the result directly when data-first, or a reusable matcher when
203
+ * curried.
204
+ *
205
+ * @param handler - fallback run on the input when no prior case matched
206
+ * @returns the result (data-first) or a matcher function (curried)
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * type Event =
211
+ * | { type: "message"; content: string }
212
+ * | { type: "error"; message: string }
213
+ * | { type: "done" };
214
+ *
215
+ * const len = match({ type: "done" } as Event)
216
+ * .with({ type: "message" }, (e) => e.content.length)
217
+ * .otherwise(() => -1); // -1
218
+ * ```
219
+ */
220
+ otherwise(handler: (input: T) => R): HasInput extends true ? R : (input: T) => R;
221
+ }
222
+ /**
223
+ * Start a pattern-matching expression. Two call shapes:
224
+ *
225
+ * - `match(value)` — data-first; subsequent `.exhaustive()` or
226
+ * `.otherwise(fn)` runs immediately against `value`.
227
+ * - `match<T>()` — curried; subsequent `.exhaustive()` returns a function
228
+ * `(value: T) => R` for use in `pipe(...)` or as a reusable matcher.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * // Data-first
233
+ * const out = match({ kind: "click", x: 1, y: 2 } as Event)
234
+ * .with({ kind: "click" }, (e) => e.x + e.y)
235
+ * .with({ kind: "key" }, () => -1)
236
+ * .exhaustive();
237
+ *
238
+ * // Curried — build a reusable matcher
239
+ * const describe = match<Event>()
240
+ * .with({ kind: "click" }, (e) => `click @ ${e.x},${e.y}`)
241
+ * .with({ kind: "key" }, (e) => `key ${e.code}`)
242
+ * .exhaustive();
243
+ * ```
244
+ */
245
+ export declare function match<T>(input: T): MatchBuilder<T, never, true>;
246
+ export declare function match<T = never>(): MatchBuilder<T, never, false>;
247
+ export {};