unthrown 3.0.1 → 4.0.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/dist/index.d.cts CHANGED
@@ -16,13 +16,37 @@ type Prettify<T> = { [K in keyof T]: T[K] } & {};
16
16
  */
17
17
  type Bound<T, K extends string, U> = Prettify<Omit<T, K> & { readonly [P in K]: U }>;
18
18
  /**
19
- * The method surface every {@link Result} variant carries. Factored out so the
20
- * three variants ({@link OkView}, {@link ErrView}, {@link DefectView}) can each
21
- * intersect it. Not part of the public API on its own.
19
+ * Compile-time rejection of a thenable callback result — the type-level
20
+ * enforcement of "combinator callbacks are synchronous" (see the
21
+ * {@link AsyncResult} remarks).
22
+ *
23
+ * @remarks
24
+ * Resolves to `unknown` (a no-op in an intersection) for any non-thenable `R`,
25
+ * and to an explanatory string-literal type when `R` is a `PromiseLike` — so an
26
+ * `async` callback fails to compile with the explanation in the error. Without
27
+ * this, `async () => …` would be assignable to `() => void`, and its rejection
28
+ * would escape the pipeline as an unhandled rejection instead of a `Defect`.
29
+ * Lift async work with {@link fromPromise} and compose it with `flatMap`.
30
+ *
31
+ * @typeParam R - the callback's inferred return type.
32
+ * @category Types
33
+ */
34
+ type NotThenable<R> = [R] extends [PromiseLike<unknown>] ? "unthrown: combinator callbacks are synchronous — lift async work with fromPromise and compose with flatMap" : unknown;
35
+ /**
36
+ * The fluent method surface every {@link Result} variant carries — the
37
+ * combinators (`map`, `flatMap`, `mapErr`, `match`, `unwrap`, …), documented one
38
+ * per entry below. Factored out so the three variants ({@link OkView},
39
+ * {@link ErrView}, {@link DefectView}) can each intersect it; {@link AsyncResult}
40
+ * mirrors this surface with async signatures.
41
+ *
42
+ * @remarks
43
+ * This type exists to **document** the surface and to power narrowing — not to be
44
+ * authored against. You obtain it by holding a `Result` (or `AsyncResult`), never
45
+ * by implementing your own `Result`-like; treat it as read-only reference.
22
46
  *
23
47
  * @typeParam T - the success value type.
24
48
  * @typeParam E - the modeled error type.
25
- * @internal
49
+ * @category Methods
26
50
  */
27
51
  type ResultMethods<T, E> = {
28
52
  /**
@@ -31,10 +55,12 @@ type ResultMethods<T, E> = {
31
55
  * Runs `f` only on `Ok`; `Err` and `Defect` pass through untouched. If `f`
32
56
  * throws, the thrown value is captured as a `Defect`.
33
57
  *
58
+ * An async callback is rejected at compile time ({@link NotThenable}).
59
+ *
34
60
  * @typeParam U - the mapped success type.
35
61
  * @param f - maps the current success value to a new one.
36
62
  */
37
- map<U>(f: (value: T) => U): Result$1<U, E>;
63
+ map<U>(f: (value: T) => U & NotThenable<U>): Result$1<U, E>;
38
64
  /**
39
65
  * Sequence a dependent, `Result`-returning step (monadic bind).
40
66
  *
@@ -50,11 +76,12 @@ type ResultMethods<T, E> = {
50
76
  * Run a side effect on the success value and pass the `Result` through
51
77
  * unchanged.
52
78
  *
53
- * Runs only on `Ok`. If `f` throws, the throw becomes a `Defect`.
79
+ * Runs only on `Ok`. If `f` throws, the throw becomes a `Defect`. An async
80
+ * callback is rejected at compile time ({@link NotThenable}).
54
81
  *
55
82
  * @param f - the side effect (its return value is ignored).
56
83
  */
57
- tap(f: (value: T) => void): Result$1<T, E>;
84
+ tap<R>(f: (value: T) => R & NotThenable<R>): Result$1<T, E>;
58
85
  /**
59
86
  * Run a **failable** side effect on the success value, keeping the original
60
87
  * value but threading the effect's error.
@@ -99,14 +126,15 @@ type ResultMethods<T, E> = {
99
126
  * @remarks
100
127
  * `f` receives the scope and returns a value (not a `Result`); it is added as
101
128
  * `{ ...scope, [name]: value }`. Runs only on `Ok`; `Err`/`Defect` pass
102
- * through. A throw becomes a `Defect`.
129
+ * through. A throw becomes a `Defect`. An async callback is rejected at
130
+ * compile time ({@link NotThenable}).
103
131
  *
104
132
  * @typeParam K - the key the value is stored under.
105
133
  * @typeParam U - the value type.
106
134
  * @param name - the scope key.
107
135
  * @param f - computes a value from the accumulated scope.
108
136
  */
109
- let<K extends string, U>(name: K, f: (scope: T) => U): Result$1<Bound<T, K, U>, E>;
137
+ let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): Result$1<Bound<T, K, U>, E>;
110
138
  /**
111
139
  * Replace the success value with a constant `value`.
112
140
  *
@@ -119,12 +147,13 @@ type ResultMethods<T, E> = {
119
147
  * Transform the modeled error with `f`.
120
148
  *
121
149
  * Runs `f` only on `Err`; `Ok` passes through and a `Defect` is **never**
122
- * touched. If `f` throws, the throw becomes a `Defect`.
150
+ * touched. If `f` throws, the throw becomes a `Defect`. An async callback is
151
+ * rejected at compile time ({@link NotThenable}).
123
152
  *
124
153
  * @typeParam E2 - the mapped error type.
125
154
  * @param f - maps the current error to a new one.
126
155
  */
127
- mapErr<E2>(f: (error: E) => E2): Result$1<T, E2>;
156
+ mapErr<E2>(f: (error: E) => E2 & NotThenable<E2>): Result$1<T, E2>;
128
157
  /**
129
158
  * Recover from an `Err` by producing another `Result`.
130
159
  *
@@ -144,20 +173,24 @@ type ResultMethods<T, E> = {
144
173
  * The result type is `Result<T | U, never>`, but `never` describes only the
145
174
  * **error** channel — a `Defect` can still be present at runtime, so do not
146
175
  * read `never` as "total". Runs `f` only on `Err`; `Ok` and `Defect` pass
147
- * through. If `f` throws, the throw becomes a `Defect`.
176
+ * through. If `f` throws, the throw becomes a `Defect`. An async callback is
177
+ * rejected at compile time ({@link NotThenable}).
148
178
  *
149
179
  * @typeParam U - the recovered success type.
150
180
  * @param f - produces a success value from the current error.
151
181
  */
152
- recover<U>(f: (error: E) => U): Result$1<T | U, never>;
182
+ recover<U>(f: (error: E) => U & NotThenable<U>): Result$1<T | U, never>;
153
183
  /**
154
184
  * Run a side effect on the error and pass the `Result` through unchanged.
155
185
  *
156
- * Runs only on `Err`. If `f` throws, the throw becomes a `Defect`.
186
+ * Runs only on `Err`. If `f` throws, the result is a `Defect` whose cause is
187
+ * an `AggregateError` of `[thrown, original failure]` — observing a failure
188
+ * never destroys it. An async callback is rejected at compile time
189
+ * ({@link NotThenable}).
157
190
  *
158
191
  * @param f - the side effect (its return value is ignored).
159
192
  */
160
- tapErr(f: (error: E) => void): Result$1<T, E>;
193
+ tapErr<R>(f: (error: E) => R & NotThenable<R>): Result$1<T, E>;
161
194
  /**
162
195
  * Run a **failable** side effect on the error, keeping the original error but
163
196
  * threading the effect's own error.
@@ -167,9 +200,10 @@ type ResultMethods<T, E> = {
167
200
  * returns a `Result`, but its **success value is discarded** — on the effect's
168
201
  * `Ok` the original `Err` flows through unchanged, while an `Err` (or `Defect`)
169
202
  * from `f` short-circuits and threads its error (`Result<T, E | E2>`). Runs only
170
- * on `Err`; `Ok` and `Defect` pass through. If `f` throws, the throw becomes a
171
- * `Defect`. Use it for a failable effect _during_ error handling (e.g. writing
172
- * the error to an audit log that may itself fail).
203
+ * on `Err`; `Ok` and `Defect` pass through. If `f` throws, the result is a
204
+ * `Defect` whose cause is an `AggregateError` of `[thrown, original failure]` —
205
+ * observing a failure never destroys it. Use it for a failable effect _during_
206
+ * error handling (e.g. writing the error to an audit log that may itself fail).
173
207
  *
174
208
  * @typeParam E2 - the error type the effect may introduce.
175
209
  * @param f - the failable side effect; its `Ok` value is ignored.
@@ -191,11 +225,14 @@ type ResultMethods<T, E> = {
191
225
  recoverDefect<U, E2>(f: (cause: unknown) => Result$1<U, E2>): Result$1<T | U, E | E2>;
192
226
  /**
193
227
  * Run a side effect on a present `Defect`'s cause (e.g. logging) and pass the
194
- * `Defect` through unchanged. If `f` throws, the throw becomes a new `Defect`.
228
+ * `Defect` through unchanged. If `f` throws, the result is a `Defect` whose
229
+ * cause is an `AggregateError` of `[thrown, original failure]` — observing a
230
+ * failure never destroys it. An async callback is rejected at compile time
231
+ * ({@link NotThenable}).
195
232
  *
196
233
  * @param f - the side effect over the unknown cause.
197
234
  */
198
- tapDefect(f: (cause: unknown) => void): Result$1<T, E>;
235
+ tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): Result$1<T, E>;
199
236
  /**
200
237
  * Exhaustively fold all three runtime states into a single value of type `R`.
201
238
  *
@@ -217,35 +254,51 @@ type ResultMethods<T, E> = {
217
254
  /**
218
255
  * Extract the success value.
219
256
  *
257
+ * @remarks
258
+ * Compiles only when the error channel is empty (`E = never`) — eliminate
259
+ * modeled errors first (`match` / `recover` / `orElse`), or reach for the
260
+ * `unwrapOr` / `unwrapOrElse` / `getOrNull` / `getOrUndefined` family (which
261
+ * recover an `Err`). If you get a `'this' context` type error here, that is
262
+ * the gate: the receiver still has a non-`never` error channel.
263
+ *
264
+ * `E = never` empties only the **modeled** error channel — a `Defect` can
265
+ * still be present, and `unwrap()` **rethrows its original cause** (it
266
+ * _panics_); `Result<T, never>` does not mean `unwrap()` cannot throw.
267
+ *
220
268
  * @returns the `Ok` value.
221
- * @throws On `Err`, an {@link UnwrapError} carrying the error. On a `Defect`,
222
- * re-throws the **original cause** with its original stack, so an unhandled
223
- * Defect surfaces at the global handler as the real failure.
224
269
  */
225
- unwrap(): T;
270
+ unwrap(this: Result$1<T, never>): T;
226
271
  /**
227
272
  * Extract the modeled error.
228
273
  *
274
+ * @remarks
275
+ * Compiles only when the success channel is empty (`T = never`) — eliminate
276
+ * the success case first. `T = never` is rarely the case in practice (a
277
+ * `Result` you hold usually still has a success type), so to inspect an
278
+ * error prefer an `isErr()` guard or, in tests, `@unthrown/vitest`'s
279
+ * `toBeErrWith`. A `Defect` still **rethrows its original cause** (a defect is
280
+ * a bug, not an absent value), so this does not mean `unwrapErr()` can't throw.
281
+ *
229
282
  * @returns the `Err` value.
230
- * @throws On `Ok`, an {@link UnwrapError} carrying the value. On a `Defect`,
231
- * re-throws the original cause.
232
283
  */
233
- unwrapErr(): E;
284
+ unwrapErr(this: Result$1<never, E>): E;
234
285
  /**
235
286
  * The success value, or `fallback` on `Err`.
236
287
  *
237
- * @param fallback - returned when the result is an `Err`.
288
+ * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
289
+ * @param fallback - returned when the result is an `Err` (may be a different type; the return widens to `T | U`).
238
290
  * @throws Re-throws on a `Defect` — a Defect is a bug, not an absent value, so
239
291
  * it is never silently replaced.
240
292
  */
241
- unwrapOr(fallback: T): T;
293
+ unwrapOr<U>(fallback: U): T | U;
242
294
  /**
243
295
  * The success value, or `f(error)` on `Err`.
244
296
  *
245
- * @param f - lazily computes the fallback from the error.
297
+ * @typeParam U - the fallback type (may differ from `T`; the return widens to `T | U`).
298
+ * @param f - lazily computes the fallback from the error (may return a different type; the return widens to `T | U`).
246
299
  * @throws Re-throws on a `Defect`.
247
300
  */
248
- unwrapOrElse(f: (error: E) => T): T;
301
+ unwrapOrElse<U>(f: (error: E) => U): T | U;
249
302
  /**
250
303
  * The success value, or `null` on `Err`.
251
304
  *
@@ -263,17 +316,58 @@ type ResultMethods<T, E> = {
263
316
  isDefect(): this is DefectView<T, E>; /** Lift this synchronous `Result` into an {@link AsyncResult}. */
264
317
  toAsync(): AsyncResult$1<T, E>;
265
318
  };
266
- /** The `Ok` variant of a {@link Result}: a success carrying a `value`. */
319
+ /**
320
+ * The `Ok` variant of a {@link Result}: a success carrying a `value`. This is
321
+ * what a successful `isOk` guard narrows to, making `.value` reachable. It also
322
+ * carries the shared fluent surface ({@link ResultMethods}).
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * if (r.isOk()) r.value; // r: OkView<T, E> here — .value is a T
327
+ * ```
328
+ *
329
+ * @category Types
330
+ */
267
331
  type OkView<T, E = never> = ResultMethods<T, E> & {
268
332
  readonly tag: "Ok";
269
333
  readonly value: T;
270
334
  };
271
- /** The `Err` variant of a {@link Result}: a modeled failure carrying an `error`. */
335
+ /**
336
+ * The `Err` variant of a {@link Result}: a modeled failure carrying an `error`.
337
+ * This is what a successful `isErr` guard narrows to, exposing `.error`. It also
338
+ * carries the shared fluent surface ({@link ResultMethods}).
339
+ *
340
+ * @remarks
341
+ * **Note the parameter order: `ErrView<E, T>` puts the error type _first_** — the
342
+ * reverse of the `<T, E>` order used by {@link OkView}, {@link DefectView}, and
343
+ * {@link Result} — because `Result<T, E>` narrows to `ErrView<E, T>` (the error is
344
+ * the payload the guard makes reachable). You rarely write it by hand (a failed
345
+ * `isErr()` narrows to it for you); if you do, mind the flip — `ErrView<MyError,
346
+ * MyValue>`, not `ErrView<MyValue, MyError>`.
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * if (r.isErr()) r.error; // r: ErrView<E, T> here — .error is an E
351
+ * ```
352
+ *
353
+ * @category Types
354
+ */
272
355
  type ErrView<E, T = never> = ResultMethods<T, E> & {
273
356
  readonly tag: "Err";
274
357
  readonly error: E;
275
358
  };
276
- /** The `Defect` variant of a {@link Result}: an unmodeled failure carrying a `cause`. */
359
+ /**
360
+ * The `Defect` variant of a {@link Result}: an unmodeled failure carrying a
361
+ * `cause`. This is what a successful `isDefect` guard narrows to, exposing
362
+ * `.cause`. It also carries the shared fluent surface ({@link ResultMethods}).
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * if (r.isDefect()) r.cause; // r: DefectView<T, E> here — .cause is `unknown`
367
+ * ```
368
+ *
369
+ * @category Types
370
+ */
277
371
  type DefectView<T = never, E = never> = ResultMethods<T, E> & {
278
372
  readonly tag: "Defect";
279
373
  readonly cause: unknown;
@@ -324,107 +418,233 @@ type Result$1<T, E> = OkView<T, E> | ErrView<E, T> | DefectView<T, E>;
324
418
  * An {@link AsyncResult}'s internal promise never rejects, so `await`-ing one
325
419
  * always yields a {@link Result} and never throws — there is no rejection
326
420
  * channel to model, and none is advertised. At runtime it is still a thenable
327
- * (the only way `await` can collapse it); the narrowing simply keeps it from
328
- * being treated as a raw promise (e.g. dropped into `Promise.all`).
421
+ * (the only way `await` can collapse it), and `Promise.all` / `Promise.resolve`
422
+ * will still adopt it — harmlessly, since it settles to a `Result` and never
423
+ * rejects. What the narrowing prevents is treating it as a full promise:
424
+ * `.catch()` / `.finally()` do not type-check, because there is no rejection to
425
+ * handle.
329
426
  *
330
427
  * @typeParam T - the value `await` resolves to.
428
+ *
429
+ * @category Types
331
430
  */
332
431
  type Awaitable<T> = {
333
432
  then<R = T>(onfulfilled?: ((value: T) => R | PromiseLike<R>) | null): PromiseLike<R>;
334
433
  };
335
434
  /**
336
- * The asynchronous counterpart of {@link Result}: an awaitable wrapper with the
337
- * same method surface, collapsing to a `Result<T, E>` when `await`-ed.
435
+ * The async method surface every {@link AsyncResult} carries — the combinators
436
+ * (`map`, `flatMap`, `mapErr`, `match`, `unwrap`, …) with their asynchronous
437
+ * signatures, documented one per entry below. The async mirror of
438
+ * {@link ResultMethods}: each entry links its synchronous counterpart and states
439
+ * only the async delta.
338
440
  *
339
441
  * @remarks
340
- * **Combinator callbacks are synchronous.** A raw `Promise` may never enter an
341
- * `AsyncResult` method — that would be an un-qualified async boundary, and its
342
- * rejection would silently become a `Defect`, skipping the triage that
343
- * {@link fromPromise} forces. To do further async work, re-enter through a
344
- * qualified boundary and compose it: `ar.flatMap((v) => fromPromise(work(v),
345
- * qualify))`. The eliminators (`unwrap`, …) return promises; the binds
346
- * (`flatMap`, `flatTap`, `orElse`, `recoverDefect`) additionally accept an
347
- * `AsyncResult`.
348
- *
349
- * To pattern-match an `AsyncResult`, `await` it first: `match(await ar)`.
442
+ * Like {@link ResultMethods}, this type exists to **document** the surface — not
443
+ * to be authored against; you obtain it by holding an `AsyncResult`. Its
444
+ * combinator callbacks are **synchronous** (a raw `Promise` may never enter — see
445
+ * the {@link AsyncResult} remarks); async work re-enters via {@link fromPromise}
446
+ * and composes with `flatMap`. Systematic differences from the sync surface: the
447
+ * binds return an `AsyncResult` (and additionally accept one), and the
448
+ * eliminators return a `Promise`.
350
449
  *
351
450
  * @typeParam T - the success value type.
352
451
  * @typeParam E - the modeled error type.
452
+ * @category Methods
353
453
  */
354
- type AsyncResult$1<T, E> = Awaitable<Result$1<T, E>> & {
355
- /** Asynchronous `map`. `f` is synchronous; a throw becomes a `Defect`. */map<U>(f: (value: T) => U): AsyncResult$1<U, E>;
454
+ type AsyncResultMethods<T, E> = {
356
455
  /**
357
- * Asynchronous `flatMap`. `f` may return a `Result` **or** an `AsyncResult`
358
- * (never a raw `Promise`); a throw becomes a `Defect`.
456
+ * Asynchronous {@link ResultMethods.map | map}: transforms the success value
457
+ * with `f`. `f` is synchronous; a throw becomes a `Defect`. An async callback
458
+ * is rejected at compile time ({@link NotThenable}).
359
459
  */
360
- flatMap<U, E2>(f: (value: T) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<U, E | E2>; /** Asynchronous `tap`. `f` is synchronous; a throw becomes a `Defect`. */
361
- tap(f: (value: T) => void): AsyncResult$1<T, E>;
460
+ map<U>(f: (value: T) => U & NotThenable<U>): AsyncResult$1<U, E>;
362
461
  /**
363
- * Asynchronous `flatTap` — a failable tap that keeps the original value. `f`
364
- * may return a `Result` **or** an `AsyncResult`; its `Ok` value is discarded,
365
- * an `Err`/`Defect` short-circuits, and a throw becomes a `Defect`.
462
+ * Asynchronous {@link ResultMethods.flatMap | flatMap}. Unlike the sync form,
463
+ * `f` may return a `Result` **or** an `AsyncResult` (never a raw `Promise`); a
464
+ * throw becomes a `Defect`.
366
465
  */
367
- flatTap<E2>(f: (value: T) => Result$1<unknown, E2> | AsyncResult$1<unknown, E2>): AsyncResult$1<T, E | E2>;
466
+ flatMap<U, E2>(f: (value: T) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<U, E | E2>;
368
467
  /**
369
- * Asynchronous `bind` (do-notation). `f` may return a `Result` **or** an
370
- * `AsyncResult`; its value is bound under `name` in the accumulating scope.
468
+ * Asynchronous {@link ResultMethods.tap | tap}. `f` is synchronous; a throw
469
+ * becomes a `Defect`. An async callback is rejected at compile time
470
+ * ({@link NotThenable}).
371
471
  */
372
- bind<K extends string, U, E2>(name: K, f: (scope: T) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<Bound<T, K, U>, E | E2>; /** Asynchronous `let` (do-notation). `f` returns a plain value, bound under `name`. */
373
- let<K extends string, U>(name: K, f: (scope: T) => U): AsyncResult$1<Bound<T, K, U>, E>; /** Asynchronous `as`. */
374
- as<U>(value: U): AsyncResult$1<U, E>; /** Asynchronous `mapErr`. `f` is synchronous; a throw becomes a `Defect`. */
375
- mapErr<E2>(f: (error: E) => E2): AsyncResult$1<T, E2>; /** Asynchronous `orElse`. `f` may return a `Result` or an `AsyncResult`. */
376
- orElse<U, E2>(f: (error: E) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<T | U, E2>; /** Asynchronous `recover`. `f` is synchronous; a throw becomes a `Defect`. */
377
- recover<U>(f: (error: E) => U): AsyncResult$1<T | U, never>; /** Asynchronous `tapErr`. `f` is synchronous; a throw becomes a `Defect`. */
378
- tapErr(f: (error: E) => void): AsyncResult$1<T, E>;
472
+ tap<R>(f: (value: T) => R & NotThenable<R>): AsyncResult$1<T, E>;
379
473
  /**
380
- * Asynchronous `flatTapErr` — a failable tap on the error that keeps the
381
- * original error. `f` may return a `Result` **or** an `AsyncResult`; its `Ok`
382
- * value is discarded, an `Err`/`Defect` from `f` threads through, and a throw
474
+ * Asynchronous {@link ResultMethods.flatTap | flatTap} — a failable tap that
475
+ * keeps the original value. `f` may return a `Result` **or** an `AsyncResult`;
476
+ * its `Ok` value is discarded, an `Err`/`Defect` short-circuits, and a throw
383
477
  * becomes a `Defect`.
384
478
  */
385
- flatTapErr<E2>(f: (error: E) => Result$1<unknown, E2> | AsyncResult$1<unknown, E2>): AsyncResult$1<T, E | E2>; /** Asynchronous `recoverDefect`. `f` may return a `Result` or an `AsyncResult`. */
386
- recoverDefect<U, E2>(f: (cause: unknown) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<T | U, E | E2>; /** Asynchronous `tapDefect`. */
387
- tapDefect(f: (cause: unknown) => void): AsyncResult$1<T, E>; /** Asynchronous `match`. Handlers are synchronous; resolves to `R`. */
479
+ flatTap<E2>(f: (value: T) => Result$1<unknown, E2> | AsyncResult$1<unknown, E2>): AsyncResult$1<T, E | E2>;
480
+ /**
481
+ * Asynchronous {@link ResultMethods.bind | bind} (do-notation). `f` may return
482
+ * a `Result` **or** an `AsyncResult`; its value is bound under `name` in the
483
+ * accumulating scope.
484
+ */
485
+ bind<K extends string, U, E2>(name: K, f: (scope: T) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<Bound<T, K, U>, E | E2>;
486
+ /**
487
+ * Asynchronous {@link ResultMethods.let | let} (do-notation). `f` returns a
488
+ * plain value, bound under `name`. An async callback is rejected at compile
489
+ * time ({@link NotThenable}).
490
+ */
491
+ let<K extends string, U>(name: K, f: (scope: T) => U & NotThenable<U>): AsyncResult$1<Bound<T, K, U>, E>; /** Asynchronous {@link ResultMethods.as | as}: replaces the value with `value`. */
492
+ as<U>(value: U): AsyncResult$1<U, E>;
493
+ /**
494
+ * Asynchronous {@link ResultMethods.mapErr | mapErr}. `f` is synchronous; a
495
+ * throw becomes a `Defect`. An async callback is rejected at compile time
496
+ * ({@link NotThenable}).
497
+ */
498
+ mapErr<E2>(f: (error: E) => E2 & NotThenable<E2>): AsyncResult$1<T, E2>;
499
+ /**
500
+ * Asynchronous {@link ResultMethods.orElse | orElse}. `f` may return a `Result`
501
+ * or an `AsyncResult`.
502
+ */
503
+ orElse<U, E2>(f: (error: E) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<T | U, E2>;
504
+ /**
505
+ * Asynchronous {@link ResultMethods.recover | recover}. `f` is synchronous; a
506
+ * throw becomes a `Defect`. An async callback is rejected at compile time
507
+ * ({@link NotThenable}).
508
+ */
509
+ recover<U>(f: (error: E) => U & NotThenable<U>): AsyncResult$1<T | U, never>;
510
+ /**
511
+ * Asynchronous {@link ResultMethods.tapErr | tapErr}. `f` is synchronous; if it
512
+ * throws, the result is a `Defect` whose cause is an `AggregateError` of
513
+ * `[thrown, original failure]` — observing a failure never destroys it. An
514
+ * async callback is rejected at compile time ({@link NotThenable}).
515
+ */
516
+ tapErr<R>(f: (error: E) => R & NotThenable<R>): AsyncResult$1<T, E>;
517
+ /**
518
+ * Asynchronous {@link ResultMethods.flatTapErr | flatTapErr} — the
519
+ * error-channel mirror of `flatTap`. `f` may return a `Result` **or** an
520
+ * `AsyncResult`; its `Ok` value is discarded, an `Err`/`Defect` from `f`
521
+ * threads through, and if `f` throws, the result is a `Defect` whose cause is
522
+ * an `AggregateError` of `[thrown, original failure]` — observing a failure
523
+ * never destroys it.
524
+ */
525
+ flatTapErr<E2>(f: (error: E) => Result$1<unknown, E2> | AsyncResult$1<unknown, E2>): AsyncResult$1<T, E | E2>;
526
+ /**
527
+ * Asynchronous {@link ResultMethods.recoverDefect | recoverDefect}. `f` may
528
+ * return a `Result` or an `AsyncResult`.
529
+ */
530
+ recoverDefect<U, E2>(f: (cause: unknown) => Result$1<U, E2> | AsyncResult$1<U, E2>): AsyncResult$1<T | U, E | E2>;
531
+ /**
532
+ * Asynchronous {@link ResultMethods.tapDefect | tapDefect}. If `f` throws, the
533
+ * result is a `Defect` whose cause is an `AggregateError` of `[thrown,
534
+ * original failure]` — observing a failure never destroys it. An async
535
+ * callback is rejected at compile time ({@link NotThenable}).
536
+ */
537
+ tapDefect<R>(f: (cause: unknown) => R & NotThenable<R>): AsyncResult$1<T, E>;
538
+ /**
539
+ * Asynchronous {@link ResultMethods.match | match}. Handlers are synchronous;
540
+ * resolves to a `Promise<R>`.
541
+ */
388
542
  match<R>(cases: {
389
543
  ok: (value: T) => R;
390
544
  err: (error: E) => R;
391
545
  defect: (cause: unknown) => R;
392
- }): Promise<R>; /** Asynchronous `unwrap`. The returned promise rejects on `Err`/`Defect`. */
393
- unwrap(): Promise<T>; /** Asynchronous `unwrapErr`. */
394
- unwrapErr(): Promise<E>; /** Asynchronous `unwrapOr`. */
395
- unwrapOr(fallback: T): Promise<T>; /** Asynchronous `unwrapOrElse`. */
396
- unwrapOrElse(f: (error: E) => T): Promise<T>; /** Asynchronous `getOrNull`. */
397
- getOrNull(): Promise<T | null>; /** Asynchronous `getOrUndefined`. */
546
+ }): Promise<R>;
547
+ /**
548
+ * Asynchronous {@link ResultMethods.unwrap | unwrap}. Compiles only when the
549
+ * error channel is empty (`this: AsyncResult<T, never>`); the returned promise
550
+ * rejects on a `Defect` (rethrowing its cause).
551
+ */
552
+ unwrap(this: AsyncResult$1<T, never>): Promise<T>;
553
+ /**
554
+ * Asynchronous {@link ResultMethods.unwrapErr | unwrapErr}. Compiles only when
555
+ * the success channel is empty (`this: AsyncResult<never, E>`); the returned
556
+ * promise rejects on a `Defect` (rethrowing its cause).
557
+ */
558
+ unwrapErr(this: AsyncResult$1<never, E>): Promise<E>; /** Asynchronous {@link ResultMethods.unwrapOr | unwrapOr}. */
559
+ unwrapOr<U>(fallback: U): Promise<T | U>; /** Asynchronous {@link ResultMethods.unwrapOrElse | unwrapOrElse}. */
560
+ unwrapOrElse<U>(f: (error: E) => U): Promise<T | U>; /** Asynchronous {@link ResultMethods.getOrNull | getOrNull}. */
561
+ getOrNull(): Promise<T | null>; /** Asynchronous {@link ResultMethods.getOrUndefined | getOrUndefined}. */
398
562
  getOrUndefined(): Promise<T | undefined>;
399
563
  };
400
564
  /**
401
- * Extract the success type `T` from a `Result`.
565
+ * The asynchronous counterpart of {@link Result}: an awaitable wrapper carrying
566
+ * the {@link AsyncResultMethods} surface, collapsing to a `Result<T, E>` when
567
+ * `await`-ed.
568
+ *
569
+ * @remarks
570
+ * **Combinator callbacks are synchronous.** A raw `Promise` may never enter an
571
+ * `AsyncResult` method — that would be an un-qualified async boundary, and its
572
+ * rejection would silently become a `Defect`, skipping the triage that
573
+ * {@link fromPromise} forces. To do further async work, re-enter through a
574
+ * qualified boundary and compose it: `ar.flatMap((v) => fromPromise(work(v),
575
+ * qualify))`. The eliminators (`unwrap`, …) return promises; the binds
576
+ * (`flatMap`, `flatTap`, `orElse`, `recoverDefect`) additionally accept an
577
+ * `AsyncResult`. Its combinators are documented one per entry on
578
+ * {@link AsyncResultMethods}.
579
+ *
580
+ * To pattern-match an `AsyncResult`, `await` it first: `match(await ar)`.
581
+ *
582
+ * @typeParam T - the success value type.
583
+ * @typeParam E - the modeled error type.
584
+ */
585
+ type AsyncResult$1<T, E> = Awaitable<Result$1<T, E>> & AsyncResultMethods<T, E>;
586
+ /**
587
+ * Extract the success type `T` from a `Result` type — derive one type from
588
+ * another instead of restating it (e.g. the payload a function returns).
402
589
  *
403
590
  * @typeParam R - the `Result` type to inspect.
591
+ *
592
+ * @example
593
+ * ```ts
594
+ * type R = Result<User, NotFound>;
595
+ * type U = OkOf<R>; // User
596
+ * type E = ErrOf<R>; // NotFound
597
+ * ```
598
+ *
599
+ * @category Types
404
600
  */
405
601
  type OkOf<R> = R extends {
406
602
  readonly tag: "Ok";
407
603
  readonly value: infer T;
408
604
  } ? T : never;
409
605
  /**
410
- * Extract the error type `E` from a `Result`.
606
+ * Extract the error type `E` from a `Result` type — the counterpart of
607
+ * {@link OkOf}.
411
608
  *
412
609
  * @typeParam R - the `Result` type to inspect.
610
+ *
611
+ * @example
612
+ * ```ts
613
+ * type E = ErrOf<Result<User, NotFound>>; // NotFound
614
+ * ```
615
+ *
616
+ * @category Types
413
617
  */
414
618
  type ErrOf<R> = R extends {
415
619
  readonly tag: "Err";
416
620
  readonly error: infer E;
417
621
  } ? E : never;
418
622
  /**
419
- * Extract the success type `T` from an {@link AsyncResult}.
623
+ * Extract the success type `T` from an {@link AsyncResult} type — the async
624
+ * counterpart of {@link OkOf}.
420
625
  *
421
626
  * @typeParam R - the `AsyncResult` type to inspect.
627
+ *
628
+ * @example
629
+ * ```ts
630
+ * type T = AsyncOkOf<AsyncResult<User, NotFound>>; // User
631
+ * ```
632
+ *
633
+ * @category Types
422
634
  */
423
635
  type AsyncOkOf<R> = R extends AsyncResult$1<infer T, unknown> ? T : never;
424
636
  /**
425
- * Extract the error type `E` from an {@link AsyncResult}.
637
+ * Extract the error type `E` from an {@link AsyncResult} type — the async
638
+ * counterpart of {@link ErrOf}.
426
639
  *
427
640
  * @typeParam R - the `AsyncResult` type to inspect.
641
+ *
642
+ * @example
643
+ * ```ts
644
+ * type E = AsyncErrOf<AsyncResult<User, NotFound>>; // NotFound
645
+ * ```
646
+ *
647
+ * @category Types
428
648
  */
429
649
  type AsyncErrOf<R> = R extends AsyncResult$1<unknown, infer E> ? E : never;
430
650
  //#endregion
@@ -438,8 +658,12 @@ type AsyncErrOf<R> = R extends AsyncResult$1<unknown, infer E> ? E : never;
438
658
  * @example
439
659
  * ```ts
440
660
  * import { Ok } from "unthrown";
441
- * Ok(42).unwrap(); // 42
661
+ *
662
+ * Ok(2).map((n) => n + 1); // => Ok(3)
663
+ * Ok(42).unwrap(); // => 42
442
664
  * ```
665
+ *
666
+ * @category Constructors
443
667
  */
444
668
  declare function Ok<T>(value: T): Result$1<T, never>;
445
669
  /**
@@ -451,8 +675,12 @@ declare function Ok<T>(value: T): Result$1<T, never>;
451
675
  * @example
452
676
  * ```ts
453
677
  * import { Err } from "unthrown";
454
- * Err("not_found").unwrapErr(); // "not_found"
678
+ *
679
+ * Err("not_found").map((n) => n + 1); // => Err("not_found") (map skipped)
680
+ * Err("not_found").unwrapErr(); // => "not_found"
455
681
  * ```
682
+ *
683
+ * @category Constructors
456
684
  */
457
685
  declare function Err<E>(error: E): Result$1<never, E>;
458
686
  /**
@@ -462,22 +690,61 @@ declare function Err<E>(error: E): Result$1<never, E>;
462
690
  *
463
691
  * @example
464
692
  * ```ts
465
- * import { isOk, type Result } from "unthrown";
693
+ * import { isOk, Ok, Err, type Result } from "unthrown";
694
+ *
695
+ * isOk(Ok(1)); // => true
696
+ * isOk(Err("boom")); // => false
697
+ *
466
698
  * declare const r: Result<number, string>;
467
699
  * if (isOk(r)) r.value; // number, narrowed
468
700
  * ```
701
+ *
702
+ * @category Guards
469
703
  */
470
704
  declare function isOk<T, E>(r: Result$1<T, E>): r is OkView<T, E>;
471
705
  /**
472
706
  * Type guard: narrow a {@link Result} to its `Err` variant, exposing `.error`.
473
707
  *
474
708
  * @returns `true` when `r` is `Err`.
709
+ *
710
+ * @example
711
+ * ```ts
712
+ * import { isErr, Ok, Err, type Result } from "unthrown";
713
+ *
714
+ * isErr(Err("boom")); // => true
715
+ * isErr(Ok(1)); // => false
716
+ *
717
+ * declare const r: Result<number, string>;
718
+ * if (isErr(r)) r.error; // string, narrowed
719
+ * ```
720
+ *
721
+ * @category Guards
475
722
  */
476
723
  declare function isErr<T, E>(r: Result$1<T, E>): r is ErrView<E, T>;
477
724
  /**
478
725
  * Type guard: narrow a {@link Result} to its `Defect` variant, exposing `.cause`.
479
726
  *
727
+ * @remarks
728
+ * A `Defect` has no public constructor — it only arises at a boundary (e.g. a
729
+ * callback throwing inside a combinator). This guard is how you detect one.
730
+ *
480
731
  * @returns `true` when `r` is a `Defect`.
732
+ *
733
+ * @example
734
+ * ```ts
735
+ * import { isDefect, Ok } from "unthrown";
736
+ *
737
+ * // A throw inside a combinator is captured as a Defect:
738
+ * const r = Ok(1).map(() => {
739
+ * throw new Error("boom");
740
+ * });
741
+ * isDefect(r); // => true
742
+ * isDefect(Ok(1)); // => false
743
+ *
744
+ * if (isDefect(r)) r.cause; // unknown, narrowed
745
+ * ```
746
+ *
747
+ * @category Guards
481
748
  */
482
749
  declare function isDefect<T, E>(r: Result$1<T, E>): r is DefectView<T, E>;
483
750
  //#endregion
@@ -496,7 +763,14 @@ declare function isDefect<T, E>(r: Result$1<T, E>): r is DefectView<T, E>;
496
763
  * A `Defect` is never wrapped in an `UnwrapError`: its original cause is
497
764
  * re-thrown (with its original stack) instead.
498
765
  *
766
+ * `unwrap()` and `unwrapErr()` are type-gated (`this: Result<T, never>` /
767
+ * `Result<never, E>`), so the wrong-variant branch that throws this is
768
+ * unreachable through well-typed code — it remains only as a defensive guard
769
+ * against unsound runtime misuse (e.g. an `as` cast past the gate).
770
+ *
499
771
  * @typeParam E - the type of the {@link UnwrapError.error} it carries.
772
+ *
773
+ * @category Errors
500
774
  */
501
775
  declare class UnwrapError<E = unknown> extends Error {
502
776
  /**
@@ -517,6 +791,20 @@ declare class UnwrapError<E = unknown> extends Error {
517
791
  * is not a `Result` and returns `false`.
518
792
  *
519
793
  * @returns `true` when `x` is a `Result` produced by this library.
794
+ *
795
+ * @example
796
+ * ```ts
797
+ * import { isResult, Ok } from "unthrown";
798
+ *
799
+ * isResult(Ok(1)); // => true
800
+ * isResult({ tag: "Ok" }); // => false (look-alike, wrong prototype)
801
+ * isResult(Ok(1).toAsync()); // => false (an AsyncResult is not a Result)
802
+ *
803
+ * const x: unknown = Ok(1);
804
+ * if (isResult(x)) x.match({ ok: () => 1, err: () => 0, defect: () => -1 });
805
+ * ```
806
+ *
807
+ * @category Guards
520
808
  */
521
809
  declare function isResult(x: unknown): x is Result$1<unknown, unknown>;
522
810
  //#endregion
@@ -542,6 +830,24 @@ declare function isResult(x: unknown): x is Result$1<unknown, unknown>;
542
830
  * .map(({ user, org, label }) => render(user, org, label));
543
831
  * // Result<View, NotFound>
544
832
  * ```
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * import { Do, Ok, Err } from "unthrown";
837
+ *
838
+ * // Ok path — the scope accumulates:
839
+ * Do()
840
+ * .bind("a", () => Ok(2))
841
+ * .let("b", ({ a }) => a * 10)
842
+ * .map(({ a, b }) => a + b); // => Ok(22)
843
+ *
844
+ * // Err path — the first Err short-circuits the rest:
845
+ * Do()
846
+ * .bind("a", () => Err("boom"))
847
+ * .let("b", ({ a }) => a); // => Err("boom")
848
+ * ```
849
+ *
850
+ * @category Do-notation
545
851
  */
546
852
  declare function Do(): Result$1<{}, never>;
547
853
  //#endregion
@@ -580,10 +886,16 @@ type Defect = {
580
886
  * @param value - the possibly-absent value.
581
887
  * @param onAbsent - lazily produces the error for the absent case.
582
888
  *
889
+ * @category Interop
890
+ *
583
891
  * @example
584
892
  * ```ts
585
893
  * import { fromNullable } from "unthrown";
586
- * fromNullable(map.get(key), () => "missing").unwrap();
894
+ *
895
+ * const map = new Map([["a", 1]]);
896
+ * fromNullable(map.get("a"), () => "absent").unwrapOr(0); // => 1
897
+ * fromNullable(map.get("z"), () => "absent"); // => Err("absent")
898
+ * fromNullable(0, () => "absent").unwrapOr(-1); // => 0 (falsy but present)
587
899
  * ```
588
900
  */
589
901
  declare function fromNullable<T, E>(value: T | null | undefined, onAbsent: () => E): Result$1<NonNullable<T>, E>;
@@ -612,11 +924,21 @@ declare function fromNullable<T, E>(value: T | null | undefined, onAbsent: () =>
612
924
  * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
613
925
  * @returns a function with the same arguments returning `Result<T, E>`.
614
926
  *
927
+ * @category Interop
928
+ *
615
929
  * @example
616
930
  * ```ts
617
931
  * import { fromThrowable } from "unthrown";
618
- * const parse = fromThrowable(JSON.parse, (cause, defect) => defect(cause));
619
- * parse("{}").unwrap();
932
+ *
933
+ * // Model the parse failure as an `Err`, everything unexpected as a `Defect`.
934
+ * const parse = fromThrowable(
935
+ * (text: string) => JSON.parse(text) as unknown,
936
+ * (cause, defect) =>
937
+ * cause instanceof SyntaxError ? ("invalid_json" as const) : defect(cause),
938
+ * );
939
+ *
940
+ * parse('{"ok":true}').unwrapOr(null); // => { ok: true }
941
+ * parse("nope"); // => Err("invalid_json")
620
942
  * ```
621
943
  */
622
944
  declare function fromThrowable<A extends unknown[], T, R>(fn: (...args: A) => T, qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R): (...args: A) => Result$1<T, Exclude<R, Defect>>;
@@ -642,12 +964,19 @@ declare function fromThrowable<A extends unknown[], T, R>(fn: (...args: A) => T,
642
964
  * @param qualify - triages a rejection `cause` into a modeled `E`, or marks it
643
965
  * unmodeled by returning `defect(cause)` (the helper passed as its second arg).
644
966
  *
967
+ * @category Interop
968
+ *
645
969
  * @example
646
970
  * ```ts
647
971
  * import { fromPromise } from "unthrown";
972
+ *
973
+ * // A rejection with a NotFoundError becomes a modeled `Err`; anything else a Defect.
648
974
  * const user = await fromPromise(fetchUser(id), (cause, defect) =>
649
975
  * cause instanceof NotFoundError ? ("not_found" as const) : defect(cause),
650
976
  * );
977
+ *
978
+ * if (user.isOk()) user.value; // => the fetched user
979
+ * // when fetchUser rejects with NotFoundError: user is Err("not_found")
651
980
  * ```
652
981
  */
653
982
  declare function fromPromise<T, R>(promise: Promise<T> | (() => Promise<T>), qualify: (cause: unknown, defect: (cause: unknown) => Defect) => R): AsyncResult$1<T, Exclude<R, Defect>>;
@@ -662,6 +991,17 @@ declare function fromPromise<T, R>(promise: Promise<T> | (() => Promise<T>), qua
662
991
  *
663
992
  * @typeParam T - the resolved value type.
664
993
  * @param promise - the promise, or a thunk returning one.
994
+ *
995
+ * @category Interop
996
+ *
997
+ * @example
998
+ * ```ts
999
+ * import { fromSafePromise } from "unthrown";
1000
+ *
1001
+ * (await fromSafePromise(Promise.resolve(3))).unwrap(); // => 3
1002
+ * // a rejection becomes a Defect (never a modeled Err):
1003
+ * await fromSafePromise(Promise.reject(new Error("boom"))); // => Defect(Error("boom"))
1004
+ * ```
665
1005
  */
666
1006
  declare function fromSafePromise<T>(promise: Promise<T> | (() => Promise<T>)): AsyncResult$1<T, never>;
667
1007
  /**
@@ -698,11 +1038,14 @@ type AsyncResultRecord = Record<string, AsyncResult$1<unknown, unknown>>;
698
1038
  * collapses to `Result<T[], E>` with no cast. For a **record** keyed by name,
699
1039
  * use {@link allFromDict}.
700
1040
  *
1041
+ * @category Aggregate
1042
+ *
701
1043
  * @example
702
1044
  * ```ts
703
- * import { all, Ok } from "unthrown";
704
- * all([Ok(1), Ok("a"), Ok(true)]).unwrap(); // [1, "a", true] (typed [number, string, boolean])
705
- * all([Ok(1), Ok(2)] as Result<number, never>[]).unwrap(); // number[]
1045
+ * import { all, Ok, Err } from "unthrown";
1046
+ *
1047
+ * all([Ok(1), Ok("a"), Ok(true)]).unwrap(); // => [1, "a", true] (typed [number, string, boolean])
1048
+ * all([Ok(1), Err("e"), Ok(3)]); // => Err("e") (short-circuits on the first Err)
706
1049
  * ```
707
1050
  */
708
1051
  declare function all<Rs extends readonly Result$1<unknown, unknown>[]>(results: readonly [...Rs]): Result$1<AllOk<Rs, { [K in keyof Rs]: OkOf<Rs[K]> }>, ErrOf<Rs[number]>>;
@@ -716,10 +1059,14 @@ declare function all<Rs extends readonly Result$1<unknown, unknown>[]>(results:
716
1059
  * Same folding rules as {@link all}: first `Err` short-circuits, any `Defect`
717
1060
  * dominates. This is **not** error accumulation.
718
1061
  *
1062
+ * @category Aggregate
1063
+ *
719
1064
  * @example
720
1065
  * ```ts
721
- * import { allFromDict, Ok } from "unthrown";
722
- * allFromDict({ id: Ok(1), name: Ok("ada") }).unwrap(); // { id: 1, name: "ada" }
1066
+ * import { allFromDict, Ok, Err } from "unthrown";
1067
+ *
1068
+ * allFromDict({ id: Ok(1), name: Ok("ada") }).unwrap(); // => { id: 1, name: "ada" }
1069
+ * allFromDict({ id: Ok(1), name: Err("missing") }); // => Err("missing")
723
1070
  * ```
724
1071
  */
725
1072
  declare function allFromDict<R extends ResultRecord>(results: R): Result$1<{ [K in keyof R]: OkOf<R[K]> }, ErrOf<R[keyof R]>>;
@@ -733,10 +1080,14 @@ declare function allFromDict<R extends ResultRecord>(results: R): Result$1<{ [K
733
1080
  * short-circuits, any `Defect` dominates. As ever, the returned `AsyncResult`'s
734
1081
  * internal promise never rejects. For a **record**, use {@link allFromDictAsync}.
735
1082
  *
1083
+ * @category Aggregate
1084
+ *
736
1085
  * @example
737
1086
  * ```ts
738
1087
  * import { allAsync, fromSafePromise } from "unthrown";
739
- * await allAsync([fromSafePromise(a()), fromSafePromise(b())]);
1088
+ *
1089
+ * const both = allAsync([fromSafePromise(Promise.resolve(1)), fromSafePromise(Promise.resolve(2))]);
1090
+ * (await both).unwrap(); // => [1, 2]
740
1091
  * ```
741
1092
  */
742
1093
  declare function allAsync<Rs extends readonly AsyncResult$1<unknown, unknown>[]>(results: readonly [...Rs]): AsyncResult$1<AllOk<Rs, { [K in keyof Rs]: AsyncOkOf<Rs[K]> }>, AsyncErrOf<Rs[number]>>;
@@ -748,10 +1099,17 @@ declare function allAsync<Rs extends readonly AsyncResult$1<unknown, unknown>[]>
748
1099
  * Resolved concurrently (order preserved), folded with the {@link all} rules,
749
1100
  * and the internal promise never rejects.
750
1101
  *
1102
+ * @category Aggregate
1103
+ *
751
1104
  * @example
752
1105
  * ```ts
753
1106
  * import { allFromDictAsync, fromSafePromise } from "unthrown";
754
- * await allFromDictAsync({ a: fromSafePromise(a()), b: fromSafePromise(b()) });
1107
+ *
1108
+ * const both = allFromDictAsync({
1109
+ * a: fromSafePromise(Promise.resolve(1)),
1110
+ * b: fromSafePromise(Promise.resolve("x")),
1111
+ * });
1112
+ * (await both).unwrap(); // => { a: 1, b: "x" }
755
1113
  * ```
756
1114
  */
757
1115
  declare function allFromDictAsync<R extends AsyncResultRecord>(results: R): AsyncResult$1<{ [K in keyof R]: AsyncOkOf<R[K]> }, AsyncErrOf<R[keyof R]>>;
@@ -774,10 +1132,12 @@ declare function allFromDictAsync<R extends AsyncResultRecord>(results: R): Asyn
774
1132
  * (`AsyncResult.fromPromise`, `AsyncResult.all`, …), grouped by what they
775
1133
  * return — a static lives in exactly one namespace.
776
1134
  *
1135
+ * @category Facade
1136
+ *
777
1137
  * @example
778
1138
  * ```ts
779
1139
  * import { Result } from "unthrown";
780
- * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).unwrap(); // 2
1140
+ * Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).unwrap(); // => 2
781
1141
  * ```
782
1142
  */
783
1143
  declare const Result: {
@@ -793,6 +1153,20 @@ declare const Result: {
793
1153
  readonly isDefect: typeof isDefect;
794
1154
  readonly isResult: typeof isResult;
795
1155
  };
1156
+ /**
1157
+ * `Result<T, E>` — the core discriminated union. Shares its name with the
1158
+ * {@link Result | companion object} above (the value and type are one name); this
1159
+ * is the type half.
1160
+ *
1161
+ * @remarks
1162
+ * A `Result` is a discriminated union, so TypeDoc can't list its methods on this
1163
+ * alias. Its fluent combinators (`map`, `flatMap`, `match`, `unwrap`, …) are
1164
+ * documented one per entry on {@link ResultMethods} — the shared method surface
1165
+ * every variant carries. For "which one do I reach for?", see the
1166
+ * [Choosing a combinator](/guide/choosing-a-combinator) guide.
1167
+ *
1168
+ * @category Facade
1169
+ */
796
1170
  type Result<T, E> = Result$1<T, E>;
797
1171
  /**
798
1172
  * Companion object grouping the **`AsyncResult`-producing** entry points under
@@ -809,10 +1183,13 @@ type Result<T, E> = Result$1<T, E>;
809
1183
  * {@link Result}, the free functions remain the primary, tree-shakeable API; the
810
1184
  * value `AsyncResult` and the type {@link AsyncResult} share one name.
811
1185
  *
1186
+ * @category Facade
1187
+ *
812
1188
  * @example
813
1189
  * ```ts
814
1190
  * import { AsyncResult } from "unthrown";
815
1191
  * const user = await AsyncResult.fromPromise(fetchUser(id), (c, defect) => defect(c));
1192
+ * user.unwrap(); // => the fetched user (on success)
816
1193
  * ```
817
1194
  */
818
1195
  declare const AsyncResult: {
@@ -821,6 +1198,19 @@ declare const AsyncResult: {
821
1198
  readonly all: typeof allAsync;
822
1199
  readonly allFromDict: typeof allFromDictAsync;
823
1200
  };
1201
+ /**
1202
+ * `AsyncResult<T, E>` — the async counterpart of {@link Result}. Shares its name
1203
+ * with the {@link AsyncResult | companion object} above (value and type are one
1204
+ * name); this is the type half.
1205
+ *
1206
+ * @remarks
1207
+ * `AsyncResult` carries the async fluent surface; its combinators (`map`,
1208
+ * `flatMap`, `match`, `unwrap`, …) are documented one per entry — with their
1209
+ * async signatures — on {@link AsyncResultMethods}. For "which one do I reach
1210
+ * for?", see the [Choosing a combinator](/guide/choosing-a-combinator) guide.
1211
+ *
1212
+ * @category Facade
1213
+ */
824
1214
  type AsyncResult<T, E> = AsyncResult$1<T, E>;
825
1215
  //#endregion
826
1216
  //#region src/tagged.d.ts
@@ -831,8 +1221,10 @@ type Props = Record<string, unknown>;
831
1221
  *
832
1222
  * @typeParam Tag - the string literal discriminant.
833
1223
  * @typeParam A - the payload object type.
1224
+ *
1225
+ * @category Types
834
1226
  */
835
- type TaggedErrorInstance<Tag extends string, A extends Props> = Error & Readonly<A> & {
1227
+ type TaggedErrorInstance<Tag extends string, A extends Props> = Error & Readonly<Omit<A, "name" | "message">> & {
836
1228
  readonly _tag: Tag;
837
1229
  };
838
1230
  /**
@@ -841,12 +1233,23 @@ type TaggedErrorInstance<Tag extends string, A extends Props> = Error & Readonly
841
1233
  *
842
1234
  * @remarks
843
1235
  * When the payload is empty, the constructor takes **no** arguments (the
844
- * `keyof A extends never ? void : A` trick); otherwise it takes the payload.
1236
+ * `keyof A extends never ? void : A` trick); otherwise it takes the payload. The
1237
+ * `name` and `message` keys are both **rejected** (`name?: never` /
1238
+ * `message?: never`) because both are reserved: `name` is the display label, and
1239
+ * `message` is the human string owned by `Error`. Set the message the standard
1240
+ * way — `override message = "…"` (or a constructor override) on the subclass —
1241
+ * never as a free-form per-call payload field. The reservations are enforced at
1242
+ * the call site, mirroring how {@link TaggedErrorInstance} excludes both.
845
1243
  *
846
1244
  * @typeParam Tag - the string literal discriminant.
1245
+ *
1246
+ * @category Types
847
1247
  */
848
1248
  type TaggedErrorConstructor<Tag extends string> = {
849
- new <A extends Props = {}>(args: keyof A extends never ? void : A): TaggedErrorInstance<Tag, A>;
1249
+ new <A extends Props = {}>(args: keyof A extends never ? void : A & {
1250
+ readonly name?: never;
1251
+ readonly message?: never;
1252
+ }): TaggedErrorInstance<Tag, A>;
850
1253
  };
851
1254
  /**
852
1255
  * Build a base class for a tagged error — a class extending `Error` with a
@@ -854,9 +1257,17 @@ type TaggedErrorConstructor<Tag extends string> = {
854
1257
  *
855
1258
  * @remarks
856
1259
  * Extend the returned class to declare a concrete error. Supply the payload with
857
- * an instantiation expression; omit it for a payload-less error. A `message`
858
- * field in the payload is forwarded to `Error`. The `_tag` always reflects
859
- * `tag` and cannot be overridden by the payload.
1260
+ * an instantiation expression; omit it for a payload-less error. The `message`
1261
+ * is **not** a payload field — it is the human string owned by `Error`, not
1262
+ * structured data, so it is reserved. Define it once per subclass the standard
1263
+ * way, `override message = "…"` (it may interpolate the payload via `this`,
1264
+ * which the base populates before the subclass field initialiser runs); a
1265
+ * payload `message` is rejected at compile time, so contextual detail lives in
1266
+ * typed fields, never baked into per-call prose. The `_tag` always reflects
1267
+ * `tag` and cannot be overridden by the payload. `name` is likewise reserved —
1268
+ * it is the display label (set it with `options.name`); a payload `name` is
1269
+ * rejected at compile time (and excluded from the instance type), so it can't
1270
+ * shadow `Error.name`.
860
1271
  *
861
1272
  * `_tag` is the discriminant used by {@link matchTags}; `Error.name` is the
862
1273
  * human-facing label in stack traces and logs. By default they coincide, but
@@ -867,11 +1278,14 @@ type TaggedErrorConstructor<Tag extends string> = {
867
1278
  * ```ts
868
1279
  * class RetryableError extends TaggedError("@my-lib/RetryableError", {
869
1280
  * name: "RetryableError",
870
- * })<{ message: string }> {}
1281
+ * }) {
1282
+ * override message = "operation failed; safe to retry";
1283
+ * }
871
1284
  *
872
- * const e = new RetryableError({ message: "boom" });
873
- * e._tag; // "@my-lib/RetryableError" — namespaced discriminant
874
- * e.name; // "RetryableError" — clean display name
1285
+ * const e = new RetryableError();
1286
+ * e._tag; // "@my-lib/RetryableError" — namespaced discriminant
1287
+ * e.name; // "RetryableError" — clean display name
1288
+ * e.message; // "operation failed; safe to retry" — the standard Error.message
875
1289
  * ```
876
1290
  *
877
1291
  * @typeParam Tag - the string literal discriminant.
@@ -879,13 +1293,15 @@ type TaggedErrorConstructor<Tag extends string> = {
879
1293
  * @param options - optional overrides. `options.name` sets `Error.name`
880
1294
  * independently of `tag` (defaults to `tag`).
881
1295
  *
1296
+ * @category Tagged errors
1297
+ *
882
1298
  * @example
883
1299
  * ```ts
884
1300
  * class NotFound extends TaggedError("NotFound") {}
885
1301
  * class HttpError extends TaggedError("HttpError")<{ status: number }> {}
886
1302
  *
887
- * new NotFound()._tag; // "NotFound"
888
- * new HttpError({ status: 500 }).status; // 500
1303
+ * new NotFound()._tag; // => "NotFound"
1304
+ * new HttpError({ status: 500 }).status; // => 500
889
1305
  * ```
890
1306
  */
891
1307
  declare function TaggedError<Tag extends string>(tag: Tag, options?: {
@@ -899,6 +1315,8 @@ declare function TaggedError<Tag extends string>(tag: Tag, options?: {
899
1315
  * @typeParam T - the success value type.
900
1316
  * @typeParam E - the tagged error union.
901
1317
  * @typeParam R - the folded result type.
1318
+ *
1319
+ * @category Types
902
1320
  */
903
1321
  type TagHandlers<T, E extends {
904
1322
  _tag: string;
@@ -908,6 +1326,14 @@ type TagHandlers<T, E extends {
908
1326
  } & { [K in E["_tag"]]: (error: Extract<E, {
909
1327
  _tag: K;
910
1328
  }>) => R };
1329
+ /**
1330
+ * The channel-handler names are reserved: an error tag named `"Ok"` or
1331
+ * `"Defect"` would collide with them inside {@link TagHandlers}, so
1332
+ * {@link matchTags} rejects such unions at the call site.
1333
+ *
1334
+ * @internal
1335
+ */
1336
+ type ReservedTagError = 'unthrown: error tags "Ok" and "Defect" are reserved by matchTags — rename the colliding tag (TaggedError\'s options.name can keep the display name)';
911
1337
  /**
912
1338
  * Exhaustively fold a {@link Result} (or {@link AsyncResult}) whose error type is
913
1339
  * a tagged union, dispatching each error to the handler matching its `_tag`.
@@ -916,7 +1342,10 @@ type TagHandlers<T, E extends {
916
1342
  * The `handlers` object must provide `Ok`, `Defect`, and exactly one function
917
1343
  * per error tag; each tag's handler receives the narrowed error variant. A
918
1344
  * missing tag is a compile error. For an `AsyncResult`, the fold resolves to a
919
- * `Promise<R>`.
1345
+ * `Promise<R>`. At runtime, an error whose `_tag` has no handler (possible only
1346
+ * outside the typed contract) is routed to the `Defect` handler — an unmodeled
1347
+ * tag is an unmodeled failure. Tags named `"Ok"` or `"Defect"` are rejected at
1348
+ * compile time.
920
1349
  *
921
1350
  * @typeParam T - the success value type.
922
1351
  * @typeParam E - the tagged error union (`E extends { _tag: string }`).
@@ -924,26 +1353,33 @@ type TagHandlers<T, E extends {
924
1353
  * @param result - the result to fold.
925
1354
  * @param handlers - one branch per channel/tag.
926
1355
  *
1356
+ * @category Tagged errors
1357
+ *
927
1358
  * @example
928
1359
  * ```ts
1360
+ * import { Ok, Err, matchTags, TaggedError, type Result } from "unthrown";
1361
+ *
929
1362
  * class NotFound extends TaggedError("NotFound") {}
930
1363
  * class Forbidden extends TaggedError("Forbidden")<{ user: string }> {}
931
1364
  *
932
- * declare const r: Result<number, NotFound | Forbidden>;
933
- * matchTags(r, {
934
- * Ok: (n) => `got ${n}`,
935
- * Defect: (cause) => `bug: ${String(cause)}`,
936
- * NotFound: () => "404",
937
- * Forbidden: (e) => `403 for ${e.user}`,
938
- * });
1365
+ * const fold = (r: Result<number, NotFound | Forbidden>) =>
1366
+ * matchTags(r, {
1367
+ * Ok: (n) => `got ${n}`,
1368
+ * Defect: (cause) => `bug: ${String(cause)}`,
1369
+ * NotFound: () => "404",
1370
+ * Forbidden: (e) => `403 for ${e.user}`,
1371
+ * });
1372
+ *
1373
+ * fold(Ok(1)); // => "got 1"
1374
+ * fold(Err(new Forbidden({ user: "ada" }))); // => "403 for ada"
939
1375
  * ```
940
1376
  */
941
1377
  declare function matchTags<T, E extends {
942
1378
  _tag: string;
943
- }, R>(result: Result$1<T, E>, handlers: TagHandlers<T, E, R>): R;
1379
+ }, R>(result: Result$1<T, E>, handlers: TagHandlers<T, E, R> & ([Extract<E["_tag"], "Ok" | "Defect">] extends [never] ? unknown : ReservedTagError)): R;
944
1380
  declare function matchTags<T, E extends {
945
1381
  _tag: string;
946
- }, R>(result: AsyncResult$1<T, E>, handlers: TagHandlers<T, E, R>): Promise<R>;
1382
+ }, R>(result: AsyncResult$1<T, E>, handlers: TagHandlers<T, E, R> & ([Extract<E["_tag"], "Ok" | "Defect">] extends [never] ? unknown : ReservedTagError)): Promise<R>;
947
1383
  //#endregion
948
- export { type AsyncErrOf, type AsyncOkOf, AsyncResult, type Awaitable, type DefectView, Do, Err, type ErrOf, type ErrView, Ok, type OkOf, type OkView, Result, type TagHandlers, TaggedError, type TaggedErrorConstructor, type TaggedErrorInstance, UnwrapError, all, allAsync, allFromDict, allFromDictAsync, fromNullable, fromPromise, fromSafePromise, fromThrowable, isDefect, isErr, isOk, isResult, matchTags };
1384
+ export { type AsyncErrOf, type AsyncOkOf, AsyncResult, type AsyncResultMethods, type Awaitable, type DefectView, Do, Err, type ErrOf, type ErrView, type NotThenable, Ok, type OkOf, type OkView, Result, type ResultMethods, type TagHandlers, TaggedError, type TaggedErrorConstructor, type TaggedErrorInstance, UnwrapError, all, allAsync, allFromDict, allFromDictAsync, fromNullable, fromPromise, fromSafePromise, fromThrowable, isDefect, isErr, isOk, isResult, matchTags };
949
1385
  //# sourceMappingURL=index.d.cts.map