Option operators: map, bind, match
Option FP (batteries included) — barrel export.
Re-exports everything from the sub-modules: core constructors (ofSome/ofNone)
and operators for use in point-free pipelines.
Functions
Section titled “Functions”Combines a tuple/array of Options, preserving heterogeneous types.
Returns the first None or a Some of all values. Like Promise.all but for Option.
Two overloads:
- Tuple overload (
readonly [IOption<unknown>, ...IOption<unknown>[]]): preserves per-position types —all([ofSome(1), ofSome('hi')])yieldsIOption<readonly [number, string]>. - Array overload (
readonly IOption<T>[]): collapses every element to a single homogeneous typeT, yieldingIOption<T[]>— fits runtime-sized arrays that are not literal tuples.
export function all<T extends readonly [IOption<unknown>, ...IOption<unknown>[]]>( options: T,): IOption< { [K in keyof T]: T[K] extends IOption<infer V> ? V : never }>;export function all<T>(options: readonly IOption<T>[]): IOption<T[]>;Defined in: option/all.ts
Example
Section titled “Example”import { all, ofSome, ofNone } from '@sandlada/result/option';import type { IOption } from '@sandlada/result';
// Tuple — heterogeneous types preserved.all([ofSome(1), ofSome('hi'), ofSome(true)]);// Some([1, 'hi', true])
// Array — runtime-sized homogeneous list.const opts: IOption<number>[] = [ofSome(1), ofSome(2), ofSome(3)];all(opts);// Some([1, 2, 3])
all([ofSome(1), ofNone(), ofSome(true)]);// Nonebind()
Section titled “bind()”Chains an Option-returning function (monadic bind for Option). On None, short-circuits.
export function bind<T, U>( fn: (value: T) => IOption<U>,): (opt: IOption<T>) => IOption<U>;Defined in: option/bind.ts
Example
Section titled “Example”import { bind } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';import { ofSome, ofNone } from '@sandlada/result/option';pipe(ofSome(21), bind(n => n > 0 ? ofSome(n * 2) : ofNone()));contains()
Section titled “contains()”Returns true if the Option is Some and the value equals target.
export function contains<T>(target: T): (opt: IOption<T>) => boolean;Defined in: option/contains.ts
Example
Section titled “Example”import { contains, ofSome } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(42), contains(42)); // truepipe(ofSome(42), contains(0)); // falsefilter()
Section titled “filter()”Returns None if the predicate returns false. Otherwise passes through unchanged.
export function filter<T>( predicate: (value: T) => boolean,): (opt: IOption<T>) => IOption<T>;Defined in: option/filter.ts
Example
Section titled “Example”import { filter, ofSome } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(42), filter(n => n > 100)); // Nonepipe(ofSome(42), filter(n => n > 0)); // Some(42)flatten()
Section titled “flatten()”Flattens a nested Option: IOption<IOption<T>> → IOption<T>.
export function flatten<T>(opt: IOption<IOption<T>>): IOption<T>;Defined in: option/flatten.ts
Example
Section titled “Example”import { flatten, ofSome } from '@sandlada/result/option';
flatten(ofSome(ofSome(42))); // Some(42)Transforms the value if Some. On None, passes through unchanged.
export function map<T, U>(fn: (value: T) => U): (opt: IOption<T>) => IOption<U>;Defined in: option/map.ts
Example
Section titled “Example”import { map, ofSome } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(5), map(x => x * 2)); // Some(10)match()
Section titled “match()”Terminal — pattern-matches on both Some and None. Supports both
positional (onSome, onNone, opt?) and object ({ some, none }, opt?)
handler shapes, matching the convention used by
match in @sandlada/result/async-option. Prefer the object form.
export function match<T, U>( onSome: (value: T) => U, onNone: () => U,): (opt: IOption<T>) => U;export function match<T, U>( onSome: (value: T) => U, onNone: () => U, opt: IOption<T>,): U;export function match<T, U>( handlers: MatchOptionHandlers<T, U>,): (opt: IOption<T>) => U;export function match<T, U>( handlers: MatchOptionHandlers<T, U>, opt: IOption<T>,): U;Defined in: option/match.ts
Example
Section titled “Example”import { match, ofSome } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
// Positional form (back-compatible):pipe(ofSome(42), match(v => `value: ${v}`, () => 'nothing')); // "value: 42"
// Object form (preferred):match({ some: v => `value: ${v}`, none: () => 'nothing' }, ofSome(42));// "value: 42"ofNone()
Section titled “ofNone()”Creates a None variant of IOption — represents absence of a value.
The returned object is a singleton — every call returns the same frozen
reference. The internal object is deep-frozen at module load, and the
IOptionNone interface marks every field readonly, so the singleton
cannot be mutated at runtime or via TypeScript.
Generic over T so the result can be substituted into any IOption<T>
slot. T defaults to unknown so a bare ofNone() slots into any
IOption<X> declaration via contextual typing — letting the surrounding
const x: IOption<number> = ofNone() flow without an explicit generic. The
T parameter is purely a type-level slot marker; the runtime payload
carries no value.
export function ofNone<T = unknown>(): IOption<T>;Defined in: option/ofNone.ts
Example
Section titled “Example”import { ofNone } from '@sandlada/result/option';import type { IOption } from '@sandlada/result';
// Contextual typing widens `unknown` to `number` here.const a: IOption<number> = ofNone();
// Explicit generic still works when no contextual type is available.const b = ofNone<string>();
// Every call returns the same frozen object.const x = ofNone<number>();const y = ofNone<string>();x === y; // trueofSome()
Section titled “ofSome()”Creates a Some variant of IOption containing a value.
export function ofSome<T>(value: T): IOption<T>;Defined in: option/ofSome.ts
Example
Section titled “Example”import { ofSome } from '@sandlada/result/option';ofSome(42); // { isSome: true, isNone: false, value: 42 }okOr()
Section titled “okOr()”Converts an IOption<T> to IResultOfT<T, E>. On Some, returns
ok(value). On None, returns err(error).
export function okOr<E>(error: E): <T>(opt: IOption<T>) => IResultOfT<T, E>;Defined in: option/okOr.ts
Example
Section titled “Example”import { okOr, ofSome, ofNone } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(42), okOr('missing')); // Ok(42)pipe(ofNone(), okOr('missing')); // Err('missing')okOrElse()
Section titled “okOrElse()”Converts an IOption<T> to IResultOfT<T, E | Error>. On Some, returns
ok(value). On None, calls errorFn() and returns err(errorFn()).
The error is computed lazily — errorFn is only called when the option is None.
Throw policy: if errorFn() throws synchronously, the thrown value is
captured as the result error. Because the thrown value can be any unknown,
the return type is widened to IResultOfT<T, E | Error> — the runtime error
may be an Error instance even when the user-declared E is something else.
Callers who want a precise error shape should wrap their errorFn body in a
try/catch and return their own E instead of relying on this catch.
The function signature accepts a synchronous () => E; passing an async
function is not supported — any returned Promise will be coerced via
err(Promise) (you almost certainly want okOrElseAsync from
@sandlada/result/promise-result instead).
export function okOrElse<E>( errorFn: () => E,): <T>(opt: IOption<T>) => IResultOfT<T, E | Error>;Defined in: option/okOrElse.ts
Example
Section titled “Example”import { okOrElse, ofSome, ofNone } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(42), okOrElse(() => 'missing')); // Ok(42)pipe(ofNone(), okOrElse(() => 'missing')); // Err('missing')
// The return type is widened to IResultOfT<T, E | Error> to reflect the// catch-block's runtime payload when `errorFn` throws.pipe(ofNone(), okOrElse(() => { throw new Error('boom'); }));orElse()
Section titled “orElse()”Falls back to an alternative IOption if the input is None.
On Some, the input is passed through unchanged.
The fallback’s value type (U) is independent of the input’s value type (T),
so a recovery can return a structurally broader or different value — the
resulting carrier is IOption<T | U>. Pattern-matching on the result narrows
to T on the original Some and to U on the recovered branch.
Curried form. The inner <T> is deferred so the option’s value type is
re-inferred at every application site — orElse(fn)(IOption<User>) and
orElse(fn)(IOption<number>) both typecheck.
export function orElse<U>(fn: () => IOption<U>): <T>(opt: IOption<T>) => IOption<T | U>;export function orElse<T, U>(fn: () => IOption<U>, opt: IOption<T>): IOption<T | U>;Defined in: option/orElse.ts
Example
Section titled “Example”import { orElse, ofSome, ofNone } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofNone(), orElse(() => ofSome(42))); // Some(42)pipe(ofSome(5), orElse(() => ofSome(42))); // Some(5) (pass-through)
// Cross-type recovery — `T = number`, `U = string`, result is `IOption<number | string>`pipe(ofSome(1), orElse(() => ofSome('anonymous')));
// Direct formorElse(() => ofSome('fallback'), ofNone()); // Some('fallback')Direct form. T is inferred from the supplied option; the result widens to T | U.
Side-effect on the Some track. Calls fn with the value and passes the
original Option through unchanged.
Throw policy: If fn throws, the Option converts to None
(canonical tap/tee policy — see AGENTS.md).
export function tap<T>(fn: (value: T) => void): (opt: IOption<T>) => IOption<T>;Defined in: option/tap.ts
Example
Section titled “Example”import { tap, ofSome } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome('hello'), tap(v => console.log('got:', v)));transpose()
Section titled “transpose()”Transposes an IOption<IResultOfT<T, E>> into IResultOfT<IOption<T>, E>.
Some(Ok(v))→Ok(Some(v))Some(Err(e))→Err(e)None→Ok(None)
export function transpose<T, E>( opt: IOption<IResultOfT<T, E>>,): IResultOfT<IOption<T>, E>;Defined in: option/transpose.ts
Example
Section titled “Example”import { transpose, ofSome, ofNone } from '@sandlada/result/option';import { ok, err } from '@sandlada/result/factories';import type { IResultOfT } from '@sandlada/result';
transpose(ofSome(ok(42))); // Ok(Some(42))transpose(ofSome(err('boom'))); // Err('boom')transpose(ofNone<IResultOfT<number, string>>()); // Ok(None)traverse()
Section titled “traverse()”Iterable overload of traverseArray: maps a generic Iterable with an
Option-returning function, short-circuiting on the first None. Useful for
generators, Set/Map, and custom iterables whose length isn’t known up front.
export function traverse<A, B>( fn: (a: A) => IOption<B>,): (items: Iterable<A>) => IOption<B[]>;export function traverse<A, B>( fn: (a: A) => IOption<B>, items: Iterable<A>,): IOption<B[]>;Defined in: option/traverseArray.ts
Example
Section titled “Example”import { traverse, ofSome } from '@sandlada/result/option';
function* gen(): IterableIterator<number> { yield 1; yield 2; yield 3; }traverse(x => ofSome(x * 2), gen()); // Some([2, 4, 6])traverseArray()
Section titled “traverseArray()”Traverses an array of elements, mapping them with a function that returns an Option.
Short-circuits and returns None if the mapping function ever returns None.
A synchronous throw from the callback — or from the iterator’s next() —
is captured as None (module policy). Otherwise, returns a Some containing
an array of the mapped values.
Two overloads:
Iterable<A>— generic stream input (generators, sets, custom iterables).readonly A[]— concrete-array input with optional(a, index)callback.
fp-ts equivalent: Array.traverse(Option.Applicative)
export function traverseArray<A, B>( fn: (a: A, i: number) => IOption<B>,): (items: readonly A[]) => IOption<B[]>;export function traverseArray<A, B>( fn: (a: A, i: number) => IOption<B>, items: readonly A[],): IOption<B[]>;Defined in: option/traverseArray.ts
Example
Section titled “Example”import { traverseArray, traverse, ofSome, ofNone } from '@sandlada/result/option';traverseArray(x => x > 0 ? ofSome(x * 2) : ofNone(), [1, 2, 3]); // Some([2, 4, 6])traverseArray(x => x > 0 ? ofSome(x * 2) : ofNone(), [1, -1, 3]); // None
// Iterable overload - use `traverse` for generators and other iterables.function* gen(): IterableIterator<number> { yield 1; yield 2; yield 3; }traverse(x => ofSome(x * 2), gen()); // Some([2, 4, 6])unwrapOr()
Section titled “unwrapOr()”Extracts the value on Some, or returns a default on None. Never throws.
The default value’s type (D) is independent of the option’s value type (T), so a
sentinel of a different shape — null, undefined, a default-value object, a string —
can be substituted at the call site without re-typing the option. The result is
T | D, narrowing to T on Some and to D on None via standard control-flow
analysis at the use site.
Curried form. The inner <T> is deferred so the option’s value type is
re-inferred at every application site — unwrapOr(default)(IOption<User>) and
unwrapOr(default)(IOption<string>) both typecheck, and both produce
T | D for their respective T.
export function unwrapOr<D>(defaultValue: D): <T>(opt: IOption<T>) => T | D;export function unwrapOr<T, D>(defaultValue: D, opt: IOption<T>): T | D;Defined in: option/unwrapOr.ts
Example
Section titled “Example”import { unwrapOr, ofSome, ofNone } from '@sandlada/result/option';import { pipe } from '@sandlada/result/composition';
pipe(ofSome(42), unwrapOr(0)); // 42pipe(ofNone(), unwrapOr(0)); // 0
// Cross-shape default — `T = number`, `D = null`, result is `number | null`pipe(ofSome(1), unwrapOr(null));
// Direct form: `T = 'high' | 'low'`, `D = 'low'`, result narrows naturallyunwrapOr('low', ofSome('high')); // 'high'unwrapOr('low', ofNone()); // 'low'Direct form. T is inferred from the supplied option.
zipWith()
Section titled “zipWith()”Combines N Options (N ≥ 2) with a function. If all are Some,
returns Some(fn(a, b, ...)). If any is None, returns None. If the callback
throws, returns None.
The arity of fn fixes the number of Options accepted — variadic via tuple
inference. The catch-all mapped-type variadic handles every arity ≥ 2 in a
single pair of overloads.
Design note (type safety): This file intentionally does NOT declare
per-arity overloads like zipWith<A, B, R>(fn: (a, b) => R): .... TypeScript’s
function-type bivariance would let a 2-argument fn match a 3-argument
overload (with the third parameter silently ignored), and a 0-argument fn
match a 2-argument curried form. The single variadic below avoids both holes:
T is inferred from fn’s actual parameter list, and the constraint
readonly [unknown, unknown, ...unknown[]] requires ≥ 2 elements.
export function zipWith<T extends readonly [unknown, unknown, ...unknown[]], R>( fn: (...args: T) => R,): (...options: { [K in keyof T]: IOption<T[K]> }) => IOption<R>;export function zipWith<T extends readonly [unknown, unknown, ...unknown[]], R>( fn: (...args: T) => R, ...options: { [K in keyof T]: IOption<T[K]> }): IOption<R>;Defined in: option/zipWith.ts
Example
Section titled “Example”import { zipWith, ofSome, ofNone } from '@sandlada/result/option';import type { IOption } from '@sandlada/result';
// Arity 2zipWith((a: number, b: string) => `${a}-${b}`)(ofSome(1), ofSome('a'));// Some('1-a')
// Arity 5zipWith( (a: number, b: number, c: number, d: number, e: number) => a + b + c + d + e,)(ofSome(1), ofSome(2), ofSome(3), ofSome(4), ofSome(5));// Some(15)
// Any None short-circuits to None.zipWith((a: number, b: number) => a + b)(ofSome(1), ofNone() as IOption<number>);// None