Result operators: map, bind, match
Sync operators — barrel export.
Re-exports all synchronous operators for working with Result values.
Functions
Section titled “Functions”Logical AND — returns other if r is success, otherwise returns the original failure.
Rust equivalent: result.and(other)
export function and<B, F>(other: IResultOfT<B, F>): <A, E>(r: IResultOfT<A, E>) => IResultOfT<B, E | F>;export function and<A, E, B, F>(other: IResultOfT<B, F>, r: IResultOfT<A, E>): IResultOfT<B, E | F>;Defined in: operators/and.ts
Example
Section titled “Example”import { and, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(1);const merged = and(ok(2), input);const value = match((n) => String(n), (e) => String(e), merged);// value === '2'
const failed = err('fail');const kept = and(ok(2), failed);// kept stays Err('fail'); and short-circuits when the carrier failsandTee()
Section titled “andTee()”Side-effect on the success track. Calls fn with the value on success
and passes the original result through unchanged. Unlike bind, fn’s return value
(a IResultOfT) is ignored — even if fn returns a failure, the original success
is preserved.
Throw policy: If fn throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error type
(canonical tap/tee policy).
Phantom types — B, F: These generics appear only to type fn’s
callback return (IResultOfT<B, F>). The operator discards fn’s
return value entirely; the produced carrier uses the input’s A, E
types. They are intentionally inert — callers who rely on B/F for
constraint resolution will be surprised.
Curried form.
export function andTee<A, B, F>( fn: (a: A) => IResultOfT<B, F>, errorFn?: (thrown: unknown) => unknown,): <E>(r: IResultOfT<A, E>) => IResultOfT<A, E>;export function andTee<A, E, B, F>( fn: (a: A) => IResultOfT<B, F>, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E,): IResultOfT<A, E>;Defined in: operators/andTee.ts
Type Param
Section titled “Type Param”A
— Input value type (carried through).
Type Param
Section titled “Type Param”B
— Phantom: callback’s success type, ignored at runtime.
Type Param
Section titled “Type Param”F
— Phantom: callback’s error type, ignored at runtime.
Type Param
Section titled “Type Param”E
— Input error type (carried through).
Example
Section titled “Example”import { andTee, match } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = ok('hello');const teed = pipe(input, andTee((_v) => ok('ignored')));const kept = match((s) => s, (e) => String(e), teed);// kept === 'hello'; andTee ignores the callback result
const ignoredErr = pipe(input, andTee((_v) => err('ignored-error')));// ignoredErr stays Ok('hello'); callback failures are ignoredDirect form.
andThrough()
Section titled “andThrough()”Side-effect on the success track that can propagate errors.
Calls fn with the value on success. If fn returns a failure, that failure
replaces the original result (error propagates). If fn returns a success,
the original result passes through unchanged.
The key difference from bind is that andThrough preserves the original
success value on success, while bind replaces it with fn’s result.
Throw policy: If fn throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error union -
e.g. andThrough(fn, thrown => new MyError(String(thrown))).
export function andThrough<A, B, F>( fn: (a: A) => IResultOfT<B, F>, errorFn?: (thrown: unknown) => unknown,): <E>(r: IResultOfT<A, E>) => IResultOfT<A, E | F>;export function andThrough<A, E, B, F>( fn: (a: A) => IResultOfT<B, F>, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E | F,): IResultOfT<A, E | F>;Defined in: operators/andThrough.ts
Example
Section titled “Example”import { andThrough, match } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = ok('data');const through = pipe( input, andThrough((v: string) => (v.length > 0 ? ok(v) : err('empty'))),);const value = match((s) => s, (e) => String(e), through);// value === 'data' when the side step succeeds
const bad = ok('');const failed = pipe(bad, andThrough((v: string) => (v.length > 0 ? ok(v) : err('empty'))));// failed is Err('empty'); andThrough propagates callback failureApplicative ap — applies a function wrapped in a Result to a value wrapped in a Result.
If either the function or the value is a failure, the first failure propagates.
The function-result and value-result are allowed to have different error
types — the combined error widens to E | F. This matches the sibling
bind operator and supports heterogeneous-error pipelines.
fp-ts equivalent: ap / ap(applyToValue, wrappedFn)
export function ap<A, B, E, F>( fnResult: IResultOfT<(a: A) => B, E>,): (result: IResultOfT<A, F>) => IResultOfT<B, E | F>;export function ap<A, B, E, F>( fnResult: IResultOfT<(a: A) => B, E>, result: IResultOfT<A, F>,): IResultOfT<B, E | F>;Defined in: operators/ap.ts
Example
Section titled “Example”import { ap, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const fnResult = ok((x: number) => x * 2);const valueResult = ok(21);const applied = ap(fnResult, valueResult);const out = match((n) => String(n), (e) => String(e), applied);// out === '42'
const fnFailed = err('fn failed');const short = ap(fnFailed, valueResult);// short stays Err('fn failed'); ap returns the first failurebimap()
Section titled “bimap()”Simultaneous map over both success and failure variants.
Throw policy: If either onOk or onErr throws, the result converts to
err(caughtError). The thrown value is captured verbatim and cast to the
declared error type, so without an errorFn the declared type is a structural
claim only. Pass errorFn to customise how the thrown value maps onto your
error union.
The curried form defers <A2, E2> to the application site so an input
with a different value or error type than the callbacks’ parameter types
still typechecks — mirrors map / mapErr’s design.
export function bimap<A, E, C, F>( onOk: (a: A) => C, onErr: (e: E) => F, errorFn?: (thrown: unknown) => unknown,): <A2 extends A, E2 extends E>(r: IResultOfT<A2, E2>) => IResultOfT<C, F>;export function bimap<A, E, C, F>( onOk: (a: A) => C, onErr: (e: E) => F, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => F,): IResultOfT<C, F>;Defined in: operators/bimap.ts
Example
Section titled “Example”import { bimap, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(21);const both = bimap((x: number) => x * 2, (e: string) => `!${e}`, input);const value = match((n) => String(n), (e) => String(e), both);// value === '42'
const failed = err('oops');const mappedErr = bimap((x: number) => x * 2, (e: string) => `!${e}`, failed);// mappedErr is Err('!oops')bind()
Section titled “bind()”Chains a result-producing function (monadic bind). On success, calls f with the value and returns its result. On failure, short-circuits. The error type widens to E | F.
Throw policy: a synchronous throw from f propagates to the caller — it is
NOT caught and converted to Err. Matches the canonical Result throw policy
(“sync throws propagate, async rejections are caught”). Use bindAsync or
wrap the call site with tryCatch if you need different semantics.
F# equivalent: Result.bind f r
export function bind<A, B, F>( f: (a: A) => IResultOfT<B, F>,): <E>(r: IResultOfT<A, E>) => IResultOfT<B, E | F>;export function bind<A, B, E, F>( f: (a: A) => IResultOfT<B, F>, r: IResultOfT<A, E>,): IResultOfT<B, E | F>;Defined in: operators/bind.ts
Example
Section titled “Example”import { bind, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok('Alice');const checked = bind((name: string) => (name.length > 0 ? ok(name) : err('required')), input);const label = match((n) => String(n), (e) => String(e), checked);// label === 'Alice'
const upstream = err('fail');const skipped = bind((name: string) => ok(name), upstream);// skipped stays Err('fail'); bind short-circuits on failurecatchErr()
Section titled “catchErr()”Converts a failure into a success value, maintaining the Result track.
The recovery handler’s return type (B) is independent of the input’s value type
(A), so the fallback can be a structurally different shape (a default-object, a
sentinel, a value derived from the error, …). The result widens to
IResultOfT<A | B, never> because the recovered branch can produce either shape —
control-flow at the use site narrows r.isSuccess to A on the original track and
r.value to A | B overall (with B reachable only through the recovery path).
This is the canonical “catch error → produce a different shape” recovery. Compare
with orElse, which requires the callback to return a new IResultOfT; catchErr
simplifies that case by lifting onErr(e) straight into Ok(...) for you.
Throw policy: a synchronous throw from onErr propagates to the caller — it
is NOT caught and converted to Err. The recovery handler is the only escape
hatch for an Err source; making it swallow its own failures would hide bugs
that should surface. This matches the policy documented on unwrapOrElse and
orThrow. Wrap your onErr body in a try/catch locally if you need a
recoverable fallback.
Curried form. The inner <A> is deferred so the input’s value type is
re-inferred at every application site — catchErr(handler)(IResultOfT<A, E>) widens
A | B per call instead of locking A at the currying boundary.
export function catchErr<B, E>( onErr: (e: E) => B,): <A>(r: IResultOfT<A, E>) => IResultOfT<A | B, never>;export function catchErr<A, B, E>( onErr: (e: E) => B, r: IResultOfT<A, E>,): IResultOfT<A | B, never>;Defined in: operators/catchErr.ts
Example
Section titled “Example”import { catchErr, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const failed = err('boom');const recovered = catchErr((e: string) => 0, failed);const value = match((n) => String(n), (e) => String(e), recovered);// value === '0'
const input = ok(42);const passthrough = catchErr((e: string) => 0, input);// passthrough stays Ok(42); catchErr passes success through unchangedDirect form. A is inferred from the supplied result; the recovery widens to
A | B. The error track collapses to never because the recovery always succeeds.
choose()
Section titled “choose()”Maps an array with a function returning a Result, and keeps only the success values.
F# equivalent: List.choose
Throw policy: pure collector with no Err channel in its return type —
a synchronous throw from fn propagates to the caller; Err values are
skipped and collection continues.
export function choose<A, B, E>( fn: (a: A) => IResultOfT<B, E>,): (items: readonly A[]) => B[];export function choose<A, B, E>( fn: (a: A) => IResultOfT<B, E>, items: readonly A[],): B[];Defined in: operators/choose.ts
Example
Section titled “Example”import { choose, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const items = [1, -2, 3];const picked = choose((x: number) => (x > 0 ? ok(x * 2) : err('neg')), items);const summary = match((n) => n * 10, (e) => 0, ok(picked.length));// picked is [2, 6]; summary === 20
const none = choose((x: number) => err('nope'), [1, 2]);// none is []; Err values are skipped, not collectedcontains()
Section titled “contains()”Returns true if the result is success and the value equals target.
Rust equivalent: result.contains(target)
export function contains<A>(target: A): <E>(r: IResultOfT<A, E>) => boolean;export function contains<A, E>(target: A, r: IResultOfT<A, E>): boolean;Defined in: operators/contains.ts
Example
Section titled “Example”import { contains, unwrapOr } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const hit = pipe(input, contains(42));const fallback = unwrapOr(false, err('missing'));// hit === true; fallback === false
const miss = pipe(ok(7), contains(42));// miss === falseexists()
Section titled “exists()”Returns true if the result is success and the predicate holds.
Rust equivalent: result.is_ok_and(predicate)
export function exists<A>(predicate: (a: A) => boolean): <E>(r: IResultOfT<A, E>) => boolean;export function exists<A, E>(predicate: (a: A) => boolean, r: IResultOfT<A, E>): boolean;Defined in: operators/exists.ts
Example
Section titled “Example”import { exists, unwrapOr } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const positive = pipe(input, exists((x: number) => x > 0));const defaultFlag = unwrapOr(false, err('n/a'));// positive === true; defaultFlag === false
const negative = pipe(ok(-1), exists((x: number) => x > 0));// negative === falseexpect()
Section titled “expect()”Panics on failure — throws a TypeError with the given message. Returns the value on success.
Pass throwingFn to customise the error class — e.g.
expect(msg, r, info => new MyError(info.message, info.value)).
Without throwingFn, a built-in TypeError is thrown.
Rust equivalent: result.expect("msg")
export function expect<A, E>(msg: string): <A2 extends A>(r: IResultOfT<A2, E>) => A2;export function expect<A, E>(msg: string, r: IResultOfT<A, E>): A;export function expect<A, E>( msg: string, r: IResultOfT<A, E>, throwingFn: (info: { message: string; value: E }) => Error,): A;Defined in: operators/expect.ts
Example
Section titled “Example”import { expect, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const mapped = map((n: number) => n + 1, input);const value = expect('should succeed', mapped);// value === 43
const failed = err('boom');void expect('should fail', failed);// throws TypeError — expect panics on failureexpectErr()
Section titled “expectErr()”Panics on success — throws a TypeError with the given message. Returns the error on failure.
Pass throwingFn to customise the error class — e.g.
expectErr(msg, r, info => new MyError(info.message, info.value)).
Without throwingFn, a built-in TypeError is thrown.
Rust equivalent: result.expect_err("msg")
export function expectErr<A, E>(msg: string): <E2 extends E>(r: IResultOfT<A, E2>) => E2;export function expectErr<A, E>(msg: string, r: IResultOfT<A, E>): E;export function expectErr<A, E>( msg: string, r: IResultOfT<A, E>, throwingFn: (info: { message: string; value: A }) => Error,): E;Defined in: operators/expectErr.ts
Example
Section titled “Example”import { expectErr, mapErr } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const wrapped = mapErr((e: string) => `[${e}]`, input);const message = expectErr('should fail', wrapped);// message === '[boom]'
const success = ok(1);void expectErr('should panic', success);// throws TypeError — expectErr panics on successfilterOrElse()
Section titled “filterOrElse()”Filters a success value with a predicate. If the predicate holds, the original
success passes through. If it fails, returns err(errorFn(value)). Failures pass through unchanged.
Rust equivalent: result.filter_or_else(errorFn, predicate)
Throw policy: If either the predicate or errorFn throws, the result converts to
err(caughtError). The thrown value is captured verbatim and cast to the
declared error type, so without a throwErrorFn the declared type is a
structural claim only. Pass throwErrorFn to customise how the thrown value
maps onto your error union.
export function filterOrElse<A, E>( predicate: (a: A) => boolean, errorFn: (a: A) => E, throwErrorFn?: (thrown: unknown) => unknown,): (r: IResultOfT<A, E>) => IResultOfT<A, E>;export function filterOrElse<A, E>( predicate: (a: A) => boolean, errorFn: (a: A) => E, r: IResultOfT<A, E>, throwErrorFn?: (thrown: unknown) => E,): IResultOfT<A, E>;Defined in: operators/filterOrElse.ts
Example
Section titled “Example”import { filterOrElse, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(5);const kept = filterOrElse( (x: number) => x > 0, (x: number) => `${x} is not positive`, input,);const value = match((n) => String(n), (e) => String(e), kept);// value === '5'
const bad = ok(-1);const rejected = filterOrElse( (x: number) => x > 0, (x: number) => `${x} is not positive`, bad,);// rejected is Err('-1 is not positive')
const upstream = err('down');const passthrough = filterOrElse((x: number) => x > 0, (x: number) => `${x}`, upstream);// passthrough stays Err('down'); filterOrElse short-circuits on failureflatten()
Section titled “flatten()”Flattens a nested result: IResultOfT<IResultOfT<A, E>, E> → IResultOfT<A, E>.
Single-step only: flatten unwraps exactly one layer. If the inner value is
itself a Result (e.g. Result<Result<Result<A, E>, E>, E>), call flatten
repeatedly until you reach the target depth.
Rust equivalent: result.flatten()
export function flatten<A, E>(r: IResultOfT<IResultOfT<A, E>, E>): IResultOfT<A, E>;Defined in: operators/flatten.ts
Example
Section titled “Example”import { flatten, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const nested = ok(ok(42));const flat = flatten(nested);const value = match((n) => String(n), (e) => String(e), flat);// value === '42'
const failed = err('outer');const outerOnly = flatten(failed);// outerOnly stays Err('outer'); flatten short-circuits on failureTransforms the success value. If the result is a failure, it is passed through unchanged.
Throw policy: If f throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error union -
e.g. map(f, e => new MyError(String(e))). An errorFn that throws itself is
collapsed the same way: the mapper’s error becomes the error of the result,
it never escapes the Result wrapper.
F# equivalent: Result.map f r
export function map<A, B>( f: (a: A) => B, errorFn?: (thrown: unknown) => unknown,): <E>(r: IResultOfT<A, E>) => IResultOfT<B, E>;export function map<A, B, E>( f: (a: A) => B, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E,): IResultOfT<B, E>;Defined in: operators/map.ts
Example
Section titled “Example”import { map, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(5);const mapped = map((x: number) => x * 2, input);const label = match((n) => `ok:${n}`, (e) => `err:${String(e)}`, mapped);// label === 'ok:10'
const failure = err('skip');const passthrough = map((x: number) => x * 2, failure);// passthrough stays Err('skip'); map short-circuits on failuremapErr()
Section titled “mapErr()”Transforms the error value. If the result is a success, it is passed through unchanged.
Throw policy: a synchronous throw from f propagates to the caller — it is
NOT caught and converted to Err. This matches the canonical Result throw
policy (“synchronous throws propagate, async rejections are caught”). If you
need f’s throw to become an Err, wrap the call site with tryCatch.
F# equivalent: Result.mapError f r
export function mapErr<E, F>(f: (e: E) => F): <A>(r: IResultOfT<A, E>) => IResultOfT<A, F>;export function mapErr<A, E, F>(f: (e: E) => F, r: IResultOfT<A, E>): IResultOfT<A, F>;Defined in: operators/mapErr.ts
Example
Section titled “Example”import { mapErr, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const wrapped = mapErr((e: string) => `[wrapped] ${e}`, input);const label = match((n) => String(n), (e) => e, wrapped);// label === '[wrapped] boom'
const success = ok(1);const passthrough = mapErr((e: string) => e, success);// passthrough stays Ok(1); mapErr short-circuits on successmapOr()
Section titled “mapOr()”Maps the success value, or returns defaultValue on failure. Equivalent to map(fn).unwrapOr(defaultValue) but more efficient.
export function mapOr<A, B, E>( defaultValue: B, fn: (a: A) => B,): (r: IResultOfT<A, E>) => B;export function mapOr<A, B, E>( defaultValue: B, fn: (a: A) => B, r: IResultOfT<A, E>,): B;Defined in: operators/mapOr.ts
Example
Section titled “Example”import { mapOr, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(5);const mapped = mapOr(-1, (x: number) => x * 2, input);const label = match((n) => String(n), (e) => String(e), ok(mapped));// label === '10'
const failed = err('boom');const fallback = mapOr(-1, (x: number) => x * 2, failed);// fallback === -1mapOrElse()
Section titled “mapOrElse()”Maps the success value, or computes a default from the error on failure. Equivalent to map(fn).unwrapOrElse(onErr) but more efficient.
export function mapOrElse<A, B, E>( onErr: (e: E) => B, fn: (a: A) => B,): (r: IResultOfT<A, E>) => B;export function mapOrElse<A, B, E>( onErr: (e: E) => B, fn: (a: A) => B, r: IResultOfT<A, E>,): B;Defined in: operators/mapOrElse.ts
Example
Section titled “Example”import { mapOrElse, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(5);const mapped = mapOrElse((e: string) => 0, (x: number) => x * 2, input);const label = match((n) => String(n), (e) => String(e), ok(mapped));// label === '10'
const failed = err('boom');const fromErr = mapOrElse((e: string) => 0, (x: number) => x * 2, failed);// fromErr === 0match()
Section titled “match()”Terminal handler — pattern-matches on both success and failure
cases. Supports both positional (onOk, onErr, r?) and object
({ ok, err }, r?) handler shapes, matching the convention used by
match in @sandlada/result/async-result. Prefer the object form for
consistency across the library.
F# equivalent: function Ok v → onOk v | Error e → onErr e
export function match<A, E, C>( onOk: (a: A) => C, onErr: (e: E) => C,): (r: IResultOfT<A, E>) => C;export function match<A, E, C>( onOk: (a: A) => C, onErr: (e: E) => C, r: IResultOfT<A, E>,): C;export function match<A, E, C>( handlers: MatchHandlers<A, E, C>,): (r: IResultOfT<A, E>) => C;export function match<A, E, C>( handlers: MatchHandlers<A, E, C>, r: IResultOfT<A, E>,): C;Defined in: operators/match.ts
Example
Section titled “Example”import { match, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const success = ok(42);const mapped = map((n: number) => n + 1, success);const fromObject = match( { ok: (v) => `success: ${v}`, err: (e) => `failure: ${String(e)}` }, mapped,);// fromObject === 'success: 43'
const failure = err('no');const fromPositional = match((v) => `ok:${v}`, (e) => `err:${String(e)}`, failure);// fromPositional === 'err:no'Logical OR — returns other if r is failure, otherwise returns the original success.
Rust equivalent: result.or(other)
The curried form defers E (the input error type) to the application site,
so the returned function preserves heterogeneous inference rather than
widening the input’s error to unknown.
export function or<A, F>(other: IResultOfT<A, F>): <E>(r: IResultOfT<A, E>) => IResultOfT<A, E | F>;export function or<A, E, F>(other: IResultOfT<A, F>, r: IResultOfT<A, E>): IResultOfT<A, E | F>;Defined in: operators/or.ts
Example
Section titled “Example”import { or, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('fail');const fallback = or(ok(2), input);const value = match((n) => String(n), (e) => String(e), fallback);// value === '2'
const success = ok(1);const kept = or(ok(2), success);// kept stays Ok(1); or keeps the carrier when it succeedsorElse()
Section titled “orElse()”Error recovery — tries an alternative path on failure. On failure, calls f with the error and its result replaces this one. On success, passes through unchanged.
Throw policy: If f throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error union.
export function orElse<E, B, F>( f: (e: E) => IResultOfT<B, F>, errorFn?: (thrown: unknown) => unknown,): <A>(r: IResultOfT<A, E>) => IResultOfT<A | B, F>;export function orElse<A, E, B, F>( f: (e: E) => IResultOfT<B, F>, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => F,): IResultOfT<A | B, F>;Defined in: operators/orElse.ts
Example
Section titled “Example”import { orElse, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const fallback = orElse((e: string) => ok('default'), input);const value = match((s) => s, (e) => String(e), fallback);// value === 'default'
const success = ok('live');const kept = orElse((_e: string) => ok('default'), success);// kept stays Ok('live'); orElse passes success through unchangedorTee()
Section titled “orTee()”Side-effect on the error track. Calls fn with the error on failure
and passes the original result through unchanged. Unlike orElse, fn’s return value
(a IResultOfT) is ignored — even if fn returns a success, the original failure
is preserved.
Throw policy: If fn throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error type
(canonical tap/tee policy - see AGENTS.md).
export function orTee<E, B, F>( fn: (e: E) => IResultOfT<B, F>, errorFn?: (thrown: unknown) => unknown,): <A>(r: IResultOfT<A, E>) => IResultOfT<A, E>;export function orTee<A, E, B, F>( fn: (e: E) => IResultOfT<B, F>, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E,): IResultOfT<A, E>;Defined in: operators/orTee.ts
Example
Section titled “Example”import { orTee, match } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const teed = pipe(input, orTee((_e) => ok('ignored')));const label = match((n) => String(n), (e) => e, teed);// label === 'boom'; orTee preserves the original failure
const ignoredErr = pipe(input, orTee((_e) => err('ignored-error')));// ignoredErr stays Err('boom'); callback results are ignoredorThrow()
Section titled “orThrow()”Unwraps a result, throwing the error on failure.
orThrow throws the error directly (requires E extends Error).
orThrowWith transforms the error via a callback before throwing.
Rust equivalent: result.unwrap() / result.expect("msg")
export function orThrow<T, E extends Error>(r: IResultOfT<T, E>): T;Defined in: operators/orThrow.ts
Example
Section titled “Example”import { orThrow, orThrowWith, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const mapped = map((n: number) => n + 1, input);const value = orThrow(mapped);// value === 43
const failed = err(new Error('boom'));void orThrow(failed);// throws Error('boom')
const soft = err('fail');void orThrowWith((e: string) => new Error(`Custom: ${e}`), soft);// throws Error('Custom: fail')orThrowWith()
Section titled “orThrowWith()”Unwraps the success value, throwing a custom error on failure.
Transforms the error via errorFn before throwing.
Data-last curried — supports both direct and partial application.
export function orThrowWith<T, E>( errorFn: (error: E) => Error,): <T2 extends T>(r: IResultOfT<T2, E>) => T2;export function orThrowWith<T, E>( errorFn: (error: E) => Error, r: IResultOfT<T, E>,): T;Defined in: operators/orThrow.ts
errorFn
Transforms the error into an Error to throw.
r
The result to unwrap (omitted for curried form).
Throws
Section titled “Throws”The transformed error if the result is a failure.
separate()
Section titled “separate()”Partitions an array of Results into two arrays: successes and errors.
Rust equivalent: results.into_iter().partition_map() / Iter::partition_result()
export function separate<T, E>(results: readonly IResultOfT<T, E>[]): { ok: T[]; err: E[] };Defined in: operators/separate.ts
Example
Section titled “Example”import { separate, unwrapOr } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const rows = [ok(1), err('a'), ok(2), err('b')];const parts = separate(rows);const firstOk = unwrapOr(0, ok(parts.ok[0] ?? 0));// parts.ok is [1, 2] and parts.err is ['a', 'b']; firstOk === 1
const empty = separate([]);// empty is { ok: [], err: [] }swap()
Section titled “swap()”Swaps success and failure: Ok(v) → Err(v), Err(e) → Ok(e).
Rust equivalent: result.swap()
export function swap<A, E>(r: IResultOfT<A, E>): IResultOfT<E, A>;Defined in: operators/swap.ts
Example
Section titled “Example”import { swap, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const flipped = swap(input);const errSide = match((n) => String(n), (e) => String(e), flipped);// errSide === '42'; Ok value moves to the error track
const failed = err('oops');const okSide = match((n) => String(n), (e) => e, swap(failed));// okSide === 'oops'; Err error moves to the success trackSide-effect on the success track. Calls fn with the value on success
and passes the original result through unchanged.
Throw policy: If fn throws, the result converts to err(caughtError).
Pass errorFn to customise how the thrown value maps onto your error type —
e.g. tap(v => persist(v), thrown => new MyError(String(thrown))) — so the
result’s error matches your domain shape (canonical tap/tee policy).
Without errorFn the thrown value is cast to the input result’s E. For a
typed E like a tagged-union MyError, prefer supplying errorFn so the
runtime payload is not silently widened.
export function tap<A>( fn: (a: A) => void, errorFn?: (thrown: unknown) => unknown,): <E>(r: IResultOfT<A, E>) => IResultOfT<A, E>;export function tap<A, E>( fn: (a: A) => void, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E,): IResultOfT<A, E>;Defined in: operators/tap.ts
Example
Section titled “Example”import { tap, match } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const input = ok('hello');const touched = pipe(input, tap((_v: string) => undefined));const kept = match((s) => s, (e) => String(e), touched);// kept === 'hello'; tap returns the original result
const failure = err('down');const passthrough = pipe(failure, tap((_v: string) => undefined));// passthrough stays Err('down'); tap skips the callback on failuretapErr()
Section titled “tapErr()”Side-effect on the failure track. Calls fn with the error on failure
and passes the original result through unchanged.
Throw policy: If fn throws, the result converts to err(caughtError).
The thrown value is captured verbatim and cast to the declared error
type, so without an errorFn the declared type is a structural claim only.
Pass errorFn to customise how the thrown value maps onto your error type
(canonical tap/tee policy - see AGENTS.md).
export function tapErr<E>( fn: (e: E) => void, errorFn?: (thrown: unknown) => unknown,): <A>(r: IResultOfT<A, E>) => IResultOfT<A, E>;export function tapErr<A, E>( fn: (e: E) => void, r: IResultOfT<A, E>, errorFn?: (thrown: unknown) => E,): IResultOfT<A, E>;Defined in: operators/tapErr.ts
Example
Section titled “Example”import { tapErr, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const logged = tapErr((_e: string) => undefined, input);const label = match((n) => String(n), (e) => e, logged);// label === 'boom'; tapErr preserves the failure
const success = ok(1);const unchanged = tapErr((_e: string) => undefined, success);// unchanged stays Ok(1); tapErr skips the callback on successtraverseArray()
Section titled “traverseArray()”Applies a Result-returning function to every element in an array and collects
the results. Short-circuits on the first failure (like Promise.all).
The callback fn receives each element and its zero-based index in the source
array — useful for debugging or for results that depend on positional context.
Rust equivalent: iter.map(fn).collect::<Result<Vec<_>, _>>()
export function traverseArray<A, B, E>( fn: (item: A, index: number) => IResultOfT<B, E>,): (items: readonly A[]) => IResultOfT<B[], E>;export function traverseArray<A, B, E>( fn: (item: A, index: number) => IResultOfT<B, E>, items: readonly A[],): IResultOfT<B[], E>;Defined in: operators/traverseArray.ts
Example
Section titled “Example”import { traverseArray, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const items = [1, 2, 3];const all = traverseArray((x: number) => (x > 0 ? ok(x * 2) : err('neg')), items);const length = match((arr) => arr.length, (e) => 0, all);// length === 3; all is Ok([2, 4, 6])
const badItems = [1, -1, 3];const stopped = traverseArray((x: number) => (x > 0 ? ok(x * 2) : err('neg')), badItems);// stopped is Err('neg'); traverseArray short-circuits on first failureunsafeUnwrap()
Section titled “unsafeUnwrap()”Throws on failure — returns the success value on success.
Unlike unwrap, this function does not constrain the error type E.
The raw error object is thrown directly (not wrapped in a TypeError).
Use with care — this is primarily a testing/escape-hatch utility.
Prefer unwrap for code paths where the error type is Error.
export function unsafeUnwrap<A, E>(r: IResultOfT<A, E>): A;Defined in: operators/unsafeUnwrap.ts
Example
Section titled “Example”import { unsafeUnwrap, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const mapped = map((n: number) => n + 1, input);const value = unsafeUnwrap(mapped);// value === 43
const failed = err('boom');void unsafeUnwrap(failed);// throws 'boom'unsafeUnwrapErr()
Section titled “unsafeUnwrapErr()”Throws on success — returns the error value on failure.
Unlike unwrapErr, this function does not constrain the error type E.
The raw success value is thrown directly (not wrapped in a TypeError).
Use with care — this is primarily a testing/escape-hatch utility.
Prefer unwrapErr for code paths where the error type is Error.
export function unsafeUnwrapErr<A, E>(r: IResultOfT<A, E>): E;Defined in: operators/unsafeUnwrapErr.ts
Example
Section titled “Example”import { unsafeUnwrapErr, mapErr } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const wrapped = mapErr((e: string) => `[${e}]`, input);const message = unsafeUnwrapErr(wrapped);// message === '[boom]'
const success = ok(42);void unsafeUnwrapErr(success);// throws 42unwrap()
Section titled “unwrap()”Panics on failure — throws a TypeError with the error payload. Returns the value on success.
Pass throwingFn to customise the error class — e.g. unwrap(r, info => new MyError(info.message, info.value)).
Without throwingFn, a built-in TypeError is thrown with String(r.error) interpolated.
Rust equivalent: result.unwrap()
export function unwrap<T, E>(r: IResultOfT<T, E>, throwingFn?: (info: { message: string; value: E }) => Error): T;Defined in: operators/unwrap.ts
Example
Section titled “Example”import { unwrap, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const mapped = map((n: number) => n + 1, input);const value = unwrap(mapped);// value === 43
const failed = err('boom');void unwrap(failed);// throws TypeError — unwrap panics when the carrier failsunwrapErr()
Section titled “unwrapErr()”Panics on success — throws a TypeError. Returns the error on failure.
Pass throwingFn to customise the error class — e.g.
unwrapErr(r, info => new MyError(info.message, info.value)).
Without throwingFn, a built-in TypeError is thrown.
Rust equivalent: result.unwrap_err()
export function unwrapErr<A, E>(r: IResultOfT<A, E>, throwingFn?: (info: { message: string; value: A }) => Error): E;Defined in: operators/unwrapErr.ts
Example
Section titled “Example”import { unwrapErr, mapErr } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = err('boom');const wrapped = mapErr((e: string) => e, input);const message = unwrapErr(wrapped);// message === 'boom'
const success = ok(1);void unwrapErr(success);// throws TypeError — unwrapErr panics on successunwrapOr()
Section titled “unwrapOr()”Extracts the value on success, or returns a default on failure. Never throws.
The A generic is widened from the literal — e.g. unwrapOr(0) infers A = number, not A = 0, so the operator composes with IResultOfT<number, E> inputs. Without this widening, bare usage locks the success type to
the literal value of the default.
F# equivalent: Result.defaultValue def r
export function unwrapOr<A>(defaultValue: A): <E>(r: IResultOfT<A, E>) => A;export function unwrapOr<A, E>(defaultValue: A, r: IResultOfT<A, E>): A;Defined in: operators/unwrapOr.ts
Example
Section titled “Example”import { unwrapOr } from '@sandlada/result/operators';import { pipe } from '@sandlada/result/composition';import { ok, err } from '@sandlada/result/factories';
const success = ok(42);const value = pipe(success, unwrapOr(0));// value === 42
const failure = err('boom');const fallback = pipe(failure, unwrapOr(0));// fallback === 0unwrapOrElse()
Section titled “unwrapOrElse()”Extracts the value on success, or computes a default from the error on failure (lazy).
Throw policy: Like match, exceptions thrown by onErr propagate to the
caller. Ensure onErr does not throw, or wrap it with tryCatch first if you need a
recoverable fallback.
F# equivalent: Result.defaultWith f r
export function unwrapOrElse<A, E>(onErr: (e: E) => A): (r: IResultOfT<A, E>) => A;export function unwrapOrElse<A, E>(onErr: (e: E) => A, r: IResultOfT<A, E>): A;Defined in: operators/unwrapOrElse.ts
Example
Section titled “Example”import { unwrapOrElse, map } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok(42);const mapped = map((n: number) => n, input);const value = unwrapOrElse((e: Error) => 0, mapped);// value === 42
const failed = err(new Error('boom'));const recovered = unwrapOrElse((e: Error) => 0, failed);// recovered === 0unzip()
Section titled “unzip()”Unzips a Result containing a tuple into a tuple of Results.
Rust equivalent: Result::unzip
export function unzip<A, B, E>( r: IResultOfT<readonly [A, B], E>,): [IResultOfT<A, E>, IResultOfT<B, E>];Defined in: operators/unzip.ts
Example
Section titled “Example”import { unzip, match } from '@sandlada/result/operators';import { ok, err } from '@sandlada/result/factories';
const input = ok([1, 'a'] as const);const [left, right] = unzip(input);const sum = match((n) => n, (e) => 0, left);// sum === 1; left is Ok(1) and right is Ok('a')
const failed = err('e');const [l, r] = unzip(failed);// l and r are both Err('e')