Result
Result<T, E> constructors, guards, and combinators for explicit, type-safe error handling.
Result<T, E> represents either success (Ok<T>) or failure (Err<E>). It’s an explicit alternative to throw/catch — the possibility of failure is part of the function’s return type, and the compiler checks that both branches are handled.
import type { Result } from "@zap-studio/monads";
Constructors
import { err, ok } from "@zap-studio/monads";
ok(42); // { ok: true, value: 42 }
err("not found"); // { ok: false, error: "not found" }
Guards
import { err, isErr, isOk, ok } from "@zap-studio/monads";
isOk(ok(1)); // true
isErr(err("x")); // true
Combinators
All combinators live under the Result namespace to avoid colliding with Option’s combinators of the same name, and are curried — compose them with pipe.
import { err, ok, pipe, Result } from "@zap-studio/monads";
pipe(
ok(2),
Result.map((n) => n * 2),
); // Ok(4)
pipe(
err("bad"),
Result.mapErr((e) => e.toUpperCase()),
); // Err("BAD")
pipe(
ok("42"),
Result.andThen((s) => (Number.isNaN(Number(s)) ? err("not a number") : ok(Number(s)))),
); // Ok(42)
pipe(
err("bad"),
Result.orElse((e) => ok(e.length)),
); // Ok(3)
pipe(
ok(1),
Result.orElse(() => ok(0)),
); // Ok(1), fallback not called
pipe(err("bad"), Result.unwrapOr(0)); // 0
pipe(
err("bad"),
Result.unwrapOrElse((e) => e.length),
); // 3
pipe(ok(42), Result.match({ ok: (n) => `got ${n}`, err: (e) => `failed: ${e}` })); // "got 42"
orElse mirrors Rust’s Result::or_else: it recovers an Err into a fallback Result, and leaves an Ok untouched without calling the fallback.
Unwrapping
Result.unwrap throws if the Result is Err, mirroring Rust’s Result::unwrap. The original error is attached as cause.
import { err, Result } from "@zap-studio/monads";
Result.unwrap(err("bad")); // throws Error("Called unwrap() on an Err value", { cause: "bad" })
Prefer Result.match, Result.unwrapOr, or Result.unwrapOrElse for control flow that shouldn’t throw.
Wrapping Throwing Functions
fromThrowable wraps a synchronous, potentially throwing function so it returns a Result instead:
import { fromThrowable } from "@zap-studio/monads";
const safeParse = fromThrowable(JSON.parse, (error) =>
error instanceof Error ? error.message : "parse failed",
);
safeParse('{"a":1}'); // Ok({ a: 1 })
safeParse("not json"); // Err("Unexpected token ...")
Called with just a function, fromThrowable uses the caught value as the Err error, typed as unknown:
const safeParse = fromThrowable(JSON.parse);
safeParse("not json"); // Err(SyntaxError: Unexpected token ...)
For asynchronous operations that can reject, see ResultAsync.