Last active
September 3, 2026 13:08
-
-
Save ojhaujjwal/836913f925f96c7c3455232f1970ddf5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Companion code for "The Error Is a Value" (part 2 of the exceptions series). | |
| // `npm run build` typechecks this file (prebuild runs `tsc --noEmit`), so the | |
| // code printed in the post cannot drift from code that compiles. | |
| // | |
| // Trade-off vs the repo's coding standards, kept deliberately: this file | |
| // mirrors the published post 1:1 for series continuity. Boolean `ok` | |
| // discriminator (the exact type part 1 printed), plain tagged-object errors | |
| // rather than `Error` subclasses, the `never` exhaustiveness idiom rather than | |
| // `casesHandled`, and a hand-rolled `pipe` with `any` in its variadic runtime. | |
| // No JSDoc: the post is the documentation. | |
| type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }; | |
| const ok = <T>(value: T): Result<T, never> => ({ ok: true, value }); | |
| const err = <E>(error: E): Result<never, E> => ({ ok: false, error }); | |
| const map = | |
| <A, B>(f: (a: A) => B) => | |
| <E>(m: Promise<Result<A, E>>): Promise<Result<B, E>> => | |
| m.then(r => (r.ok ? ok(f(r.value)) : r)); | |
| const flatMap = | |
| <A, B, E2>(f: (a: A) => Promise<Result<B, E2>>) => | |
| async <E>(m: Promise<Result<A, E>>): Promise<Result<B, E | E2>> => { | |
| const r = await m; | |
| return r.ok ? f(r.value) : err(r.error); | |
| }; | |
| const mapError = | |
| <E, F>(f: (e: E) => F) => | |
| <A>(m: Promise<Result<A, E>>): Promise<Result<A, F>> => | |
| m.then(r => (r.ok ? r : err(f(r.error)))); | |
| const match = | |
| <A, E, Out>(handlers: { ok: (value: A) => Out; err: (error: E) => Out }) => | |
| (m: Promise<Result<A, E>>): Promise<Out> => | |
| m.then(r => (r.ok ? handlers.ok(r.value) : handlers.err(r.error))); | |
| function pipe<A>(a: A): A; | |
| function pipe<A, B = never>(a: A, ab: (a: A) => B): B; | |
| function pipe<A, B = never, C = never>( | |
| a: A, | |
| ab: (a: A) => B, | |
| bc: (b: B) => C | |
| ): C; | |
| function pipe<A, B = never, C = never, D = never>( | |
| a: A, | |
| ab: (a: A) => B, | |
| bc: (b: B) => C, | |
| cd: (c: C) => D | |
| ): D; | |
| function pipe<A, B = never, C = never, D = never, Out = never>( | |
| a: A, | |
| ab: (a: A) => B, | |
| bc: (b: B) => C, | |
| cd: (c: C) => D, | |
| de: (d: D) => Out | |
| ): Out; | |
| // SAFETY: the overloads guarantee each function accepts the previous result. | |
| // TypeScript cannot express pairwise argument alignment for a variadic reduce. | |
| function pipe(a: unknown, ...fs: Array<(x: any) => unknown>): unknown { | |
| return fs.reduce((x, f) => f(x), a); | |
| } | |
| export { ok, err, map, flatMap, mapError, match, pipe }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment