Option
Option<T> constructors, guards, and combinators for explicit, type-safe null handling.
Option<T> represents either a present value (Some<T>) or its absence (None). It’s an explicit alternative to null/undefined checks — absence is part of the type, and the compiler checks that both branches are handled.
import type { Option } from "@zap-studio/monads";
Constructors
import { none, some } from "@zap-studio/monads";
some(42); // { some: true, value: 42 }
none(); // { some: false }
Guards
import { isNone, isSome, none, some } from "@zap-studio/monads";
isSome(some(1)); // true
isNone(none()); // true
Bridging From Nullable Values
import { fromNullable } from "@zap-studio/monads";
fromNullable([1, 2, 3].find((n) => n > 5)); // None
fromNullable([1, 2, 3].find((n) => n > 1)); // Some(2)
Combinators
All combinators live under the Option namespace to avoid colliding with Result’s combinators of the same name, and are curried — compose them with pipe.
import { none, Option, pipe, some } from "@zap-studio/monads";
pipe(
some(2),
Option.map((n) => n * 2),
); // Some(4)
const half = (n: number) => (n % 2 === 0 ? some(n / 2) : none());
pipe(some(4), Option.andThen(half)); // Some(2)
pipe(
none(),
Option.orElse(() => some(0)),
); // Some(0)
pipe(
some(1),
Option.orElse(() => some(0)),
); // Some(1), fallback not called
pipe(none(), Option.unwrapOr(0)); // 0
pipe(
none(),
Option.unwrapOrElse(() => 99),
); // 99
pipe(some(42), Option.match({ some: (n) => `got ${n}`, none: () => "nothing" })); // "got 42"
orElse mirrors Rust’s Option::or_else: it recovers a None into a fallback Option of the same type, and leaves a Some untouched without calling the fallback.
Unwrapping
Option.unwrap throws if the Option is None, mirroring Rust’s Option::unwrap.
import { none, Option } from "@zap-studio/monads";
Option.unwrap(none()); // throws Error("Called unwrap() on a None value")
Prefer Option.match, Option.unwrapOr, or Option.unwrapOrElse for control flow that shouldn’t throw.