Skip to content

Instantly share code, notes, and snippets.

@ojhaujjwal
Last active September 3, 2026 13:08
Show Gist options
  • Select an option

  • Save ojhaujjwal/836913f925f96c7c3455232f1970ddf5 to your computer and use it in GitHub Desktop.

Select an option

Save ojhaujjwal/836913f925f96c7c3455232f1970ddf5 to your computer and use it in GitHub Desktop.
// 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