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.
Empty input (cross-module note): the three combiners of the library give
three different types for “combine nothing” - combine([]) yields
IResultOfT<[], never>, combine/all([]) yields IResultOfT<unknown[], unknown>
and option/all([]) yields IOption<unknown[]>. The runtime behaviour is the
same everywhere (an empty success); only the static type differs.
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, match } from '@sandlada/result/option';
const combined = all([ofSome(1), ofSome(2), ofSome(3)]);const size = match((arr) => arr.length, () => 0, combined);// size === 3
const broken = all([ofSome(1), ofNone<number>(), ofSome(3)]);const lost = match((arr) => arr.length, () => 0, broken);// lost === 0 because broken is Nonebind()
Section titled “bind()”Chains an Option-returning function (monadic bind for Option). On None, short-circuits.
Data-last and curried: bind(fn)(option). The direct form
bind(fn, option) is supported as well.
export function bind<T, U>(fn: (value: T) => IOption<U>): (opt: IOption<T>) => IOption<U>;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, match, ofSome, ofNone } from '@sandlada/result/option';
const input = ofSome(21);const chained = bind((n) => (n > 0 ? ofSome(n * 2) : ofNone<number>()), input);const value = match((v) => v, () => 0, chained);// value === 42
const missing = ofNone<number>();const short = bind((n) => ofSome(n * 2), missing);// short is None; bind short-circuits on Nonecontains()
Section titled “contains()”Returns true if the Option is Some and the value equals target.
Data-last and curried: contains(target)(option). The direct form
contains(target, option) is supported as well.
export function contains<T>(target: T): (opt: IOption<T>) => boolean;export function contains<T>(target: T, opt: IOption<T>): boolean;Defined in: option/contains.ts
Example
Section titled “Example”import { contains, ofSome, ofNone } from '@sandlada/result/option';
const input = ofSome(42);const hit = contains(42, input);const miss = contains(0, input);// hit === true, miss === false
const empty = ofNone<number>();const absent = contains(42, empty);// absent === falsefilter()
Section titled “filter()”Returns None if the predicate returns false. Otherwise passes through unchanged.
Data-last and curried: filter(predicate)(option). The direct form
filter(predicate, option) is supported as well.
export function filter<T>(predicate: (value: T) => boolean): (opt: IOption<T>) => IOption<T>;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, unwrapOr } from '@sandlada/result/option';
const input = ofSome(42);const kept = filter((n) => n > 0, input);const value = unwrapOr(0, kept);// value === 42
const rejected = filter((n) => n > 100, ofSome(42));const fallback = unwrapOr(0, rejected);// fallback === 0 because filter returned Noneflatten()
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, ofNone, unwrapOr } from '@sandlada/result/option';import type { IOption } from '@sandlada/result';
const nested = ofSome(ofSome(42));const flat = flatten(nested);const value = unwrapOr(0, flat);// value === 42
const noneOuter = ofNone<IOption<number>>();const skipped = flatten(noneOuter);// skipped is NoneTransforms the value if Some. On None, passes through unchanged.
Data-last and curried: map(fn)(option). The direct form
map(fn, option) is supported as well.
export function map<T, U>(fn: (value: T) => U): (opt: IOption<T>) => IOption<U>;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, match, ofSome, ofNone } from '@sandlada/result/option';
const input = ofSome(5);const mapped = map((x: number) => x * 2, input);const label = match((v) => `value:${v}`, () => 'nothing', mapped);// label === 'value:10'
const empty = ofNone<number>();const skipped = map((x: number) => x * 2, empty);// skipped is None; map passes None through unchangedmatch()
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, ofNone } from '@sandlada/result/option';
const input = ofSome(42);const positional = match((v) => `value: ${v}`, () => 'nothing', input);// positional === 'value: 42'
const objectForm = match({ some: (v) => `value: ${v}`, none: () => 'nothing' }, ofNone());// objectForm === 'nothing'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, unwrapOr } from '@sandlada/result/option';import type { IOption } from '@sandlada/result';
const typed: IOption<number> = ofNone();const fromContext = unwrapOr(0, typed);// fromContext === 0; contextual typing slots ofNone() into IOption<number>
const explicit = ofNone<string>();const fallback = unwrapOr('default', explicit);// fallback === 'default'ofSome()
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, map } from '@sandlada/result/option';
const input = ofSome(42);const doubled = map((n) => n * 2, input);// doubled is Some(84)okOr()
Section titled “okOr()”Converts an IOption<T> to IResultOfT<T, E>. On Some, returns
ok(value). On None, returns err(error).
Data-last and curried: okOr(error)(option). The direct form
okOr(error, option) is supported as well.
export function okOr<E>(error: E): <T>(opt: IOption<T>) => IResultOfT<T, E>;export function okOr<E, T>(error: E, opt: IOption<T>): IResultOfT<T, E>;Defined in: option/okOr.ts
Example
Section titled “Example”import { okOr, ofSome, ofNone } from '@sandlada/result/option';import { match } from '@sandlada/result/operators';
const success = okOr('missing', ofSome(42));const label = match((n) => `ok:${n}`, (e) => `err:${e}`, success);// label === 'ok:42'
const failure = okOr('missing', ofNone<number>());const code = match((n) => `ok:${n}`, (e) => `err:${e}`, failure);// code === '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.
A thrown value that is not an Error instance is wrapped in one (the
original value stays available through cause), so the declared error type
holds for every value the caller can receive.
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). Bridge the async work yourself and return its settled
value from a synchronous thunk.
Data-last and curried: okOrElse(errorFn)(option). The direct form
okOrElse(errorFn, option) is supported as well.
export function okOrElse<E>(errorFn: () => E): <T>(opt: IOption<T>) => IResultOfT<T, E | Error>;export function okOrElse<E, T>(errorFn: () => E, 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 { match } from '@sandlada/result/operators';
const success = okOrElse(() => 'missing', ofSome(42));const label = match((n) => String(n), (e) => String(e), success);// label === '42'
const lazy = okOrElse(() => 'missing', ofNone<number>());const code = match((n) => String(n), (e) => String(e), lazy);// code === 'missing'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, unwrapOr } from '@sandlada/result/option';
const recovered = orElse(() => ofSome(42), ofNone<number>());const value = unwrapOr(0, recovered);// value === 42
const passthrough = orElse(() => ofSome(99), ofSome(5));const kept = unwrapOr(0, passthrough);// kept === 5; orElse leaves Some unchangedDirect 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).
Data-last and curried: tap(fn)(option). The direct form tap(fn, option)
is supported as well.
export function tap<T>(fn: (value: T) => void): (opt: IOption<T>) => IOption<T>;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, ofNone, unwrapOr } from '@sandlada/result/option';
const input = ofSome('hello');const logged = tap((v) => { void v; }, input);const text = unwrapOr('', logged);// text === 'hello'; tap passes the original Some through
const missing = ofNone<string>();const unchanged = tap((v) => { void v; }, missing);// unchanged is None; tap does not run the side effect on Nonetranspose()
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 { match } from '@sandlada/result/operators';import type { IResultOfT } from '@sandlada/result';
const wrapped = transpose(ofSome(ok(42)));const success = match((opt) => opt.isSome, (e) => false, wrapped);// wrapped is Ok(Some(42)); success === true
const errSide = transpose(ofSome(err('boom')));const failed = match((opt) => 'ok', (e) => String(e), errSide);// failed === 'boom'
const bare = transpose(ofNone<IResultOfT<number, string>>());const stillOk = match((opt) => !opt.isSome, (e) => false, bare);// bare is Ok(None); stillOk === truetraverse()
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, unwrapOr } from '@sandlada/result/option';
function* gen(): IterableIterator<number> { yield 1; yield 2; yield 3; }const doubled = traverse((x) => ofSome(x * 2), gen());const values = unwrapOr([], doubled);// values === [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, ofSome, ofNone, unwrapOr } from '@sandlada/result/option';
const doubled = traverseArray((x) => (x > 0 ? ofSome(x * 2) : ofNone()), [1, 2, 3]);const values = unwrapOr([], doubled);// values === [2, 4, 6]
const broken = traverseArray((x) => (x > 0 ? ofSome(x * 2) : ofNone()), [1, -1, 3]);const lost = unwrapOr([], broken);// broken is None; lost === []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';
const input = ofSome(42);const value = unwrapOr(0, input);// value === 42
const missing = ofNone<number>();const fallback = unwrapOr(0, missing);// fallback === 0Direct 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, unwrapOr } from '@sandlada/result/option';
const zipped = zipWith((a: number, b: string) => `${a}-${b}`, ofSome(1), ofSome('a'));const text = unwrapOr('', zipped);// text === '1-a'
const short = zipWith((a: number, b: number) => a + b, ofSome(1), ofNone<number>());// short is None when any input is None