Skip to content

Promise Result operators

Async operators — barrel export.

Re-exports eager operators for Promise of Result values.

For Promise of Option operators, see ../promise-option/index.js (subpath ./promise-option). For lifting sync Result into Promise of Result, see ../bridge-result/index.js.

Applicative ap for Promise<IResultOfT>. Applies a function wrapped in a Promise<IResultOfT> to a value wrapped in a Promise<IResultOfT>. If either is a failure, the first failure propagates.

Mirrors async-result/ap for promise-based pipelines.

Throw policy: a throwing applied function is captured as Err(thrown). A rejection of either operand stays a rejection - the outer Promise of this layer never converts a rejection into Err.

export function ap<A, B, E>(
fnResult: Promise<IResultOfT<(a: A) => B, E>>,
): (result: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<B, E>>;
export function ap<A, B, E>(
fnResult: Promise<IResultOfT<(a: A) => B, E>>,
result: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<B, E>>;

Defined in: promise-result/ap.ts

import { ap, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const fnCarrier = asyncOk((x: number) => x * 2);
const valueCarrier = asyncOk(21);
const applied = ap(fnCarrier, valueCarrier);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, applied);
// label === 'ok:42'
const brokenValue = asyncErr('x');
const shortCircuited = await ap(fnCarrier, brokenValue);
// shortCircuited is Err('x'); ap stops at the first failing operand

Maps both success and failure values of a Promise<IResultOfT<A, E>> simultaneously.

export function bimapAsync<A, E, B, F>(
onOk: (a: A) => B | Promise<B>,
onErr: (e: E) => F | Promise<F>,
): (r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<B, F>>;
export function bimapAsync<A, E, B, F>(
onOk: (a: A) => B | Promise<B>,
onErr: (e: E) => F | Promise<F>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<B, F>>;

Defined in: promise-result/bimapAsync.ts

import { bimapAsync, matchAsync } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const input = Promise.resolve(ok(5));
const mapped = bimapAsync(
(x: number) => x.toString(),
(e: string) => e.toUpperCase(),
input,
);
const okSide = await matchAsync((s) => s, (e) => e, mapped);
// okSide === '5'
const failed = Promise.resolve(err('boom'));
const remapped = await bimapAsync(
(x: number) => x.toString(),
(e: string) => e.toUpperCase(),
failed,
);
// remapped is Err('BOOM'); bimapAsync transforms the error channel on failure

Chains an async result-returning function. fn can return IResultOfT or Promise<IResultOfT>. The error type widens to E | F.

Throw policy: a synchronous throw from f propagates to the caller via the outer promise rejection (the .then handler re-throws). A rejected Promise from f propagates as a rejection. This matches the canonical AsyncResult throw policy (“sync throws and async rejections propagate”).

Compared to bridge-result bind: bindAsync works on a Promise<IResultOfT> (the source is async). bridge-result bind is the inverse — it works on a sync IResultOfT and lifts it into a Promise<IResultOfT> (the callback is async).

export function bindAsync<A, B, F>(
f: (a: A) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<B, E | F>>;
export function bindAsync<A, B, E, F>(
f: (a: A) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<B, E | F>>;

Defined in: promise-result/bindAsync.ts

import { bindAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(21);
const chained = bindAsync(
(x: number) => (x > 0 ? asyncOk(x * 2) : asyncErr('too small')),
input,
);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, chained);
// label === 'ok:42'
const rejected = asyncOk(-1);
const tooSmall = await bindAsync(
(x: number) => (x > 0 ? asyncOk(x * 2) : asyncErr('too small')),
rejected,
);
// tooSmall is Err('too small'); bindAsync returns fn's Err without re-running the success path

Side-effect on success for a Promise<IResultOfT> that can propagate errors.

Throw policy: through-family catch policy — a synchronous throw and a rejected Promise from fn both converge to Err(thrown), matching bridge-result bindThrough and the sync andThrough.

export function bindThroughAsync<A, B, F>(
fn: (a: A) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, E | F>>;
export function bindThroughAsync<A, B, E, F>(
fn: (a: A) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, E | F>>;

Defined in: promise-result/bindThroughAsync.ts

import { bindThroughAsync, matchAsync } from '@sandlada/result/promise-result';
import { ok, err, asyncOk } from '@sandlada/result/factories';
const input = Promise.resolve(ok(5));
const validated = bindThroughAsync(
(x: number) => (x > 0 ? ok(x) : err('non-positive')),
input,
);
const okLabel = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, validated);
// okLabel === 'ok:5'
const bad = asyncOk(-1);
const blocked = await bindThroughAsync(
(x: number) => (x > 0 ? ok(x) : err('non-positive')),
bad,
);
// blocked is Err('non-positive'); through-step failure replaces the success value

Async variant of catchErr for Promise<IResultOfT>.

Recovers from an error by returning a fallback value T (or a Promise resolving to T), automatically wrapping it in a resolved Ok(T).

Throw policy: Unlike tap/tapErr (which catch and convert to Err), catchErrAsync deliberately lets sync throws and async rejections from onErr propagate verbatim as Promise rejections. The recovery handler is the only escape hatch for an Err source — making it swallow its own failures would hide bugs that should surface. Wrap your recovery logic in try/catch locally if you need to swallow.

export function catchErrAsync<A, E>(
onErr: (e: E) => A | Promise<A>,
): (r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, never>>;
export function catchErrAsync<A, E>(
onErr: (e: E) => A | Promise<A>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, never>>;

Defined in: promise-result/catchErrAsync.ts

import { catchErrAsync, unwrapOr } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncErr('boom');
const recovered = catchErrAsync((_e: string) => 0, input);
const value = await unwrapOr(-1, recovered);
// value === 0 after recovery
const healthy = asyncOk(42);
const untouched = catchErrAsync((_e: string) => 0, healthy);
const kept = await unwrapOr(-1, untouched);
// kept === 42; catchErrAsync only runs on Err

Combines an array of Promise<IResultOfT> into a single Promise<IResultOfT<T[], E>>. Short-circuits on the first failure (like Promise.all).

Mirrors async-result/combine for promise-based pipelines.

export function combine<A, E>(
results: readonly Promise<IResultOfT<A, E>>[],
): Promise<IResultOfT<A[], E>>;

Defined in: promise-result/combine.ts

import { combine, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const inputs = [asyncOk(1), asyncOk(2)] as const;
const merged = combine([...inputs]);
const okLabel = await matchAsync(
(vals: number[]) => vals.join(','),
(e) => `err:${String(e)}`,
merged,
);
// okLabel === '1,2'
const partial = [asyncOk(1), asyncErr('x')] as const;
const stopped = await combine([...partial]);
// stopped is Err('x'); combine short-circuits on the first failure

Combines Promise<IResultOfT> accumulating all errors (validation aggregation). Unlike combine, this collects every error before failing.

Mirrors async-result/combineWithAllErrors.

export function combineWithAllErrors<A, E>(
results: readonly Promise<IResultOfT<A, E>>[],
): Promise<IResultOfT<A[], E[]>>;

Defined in: promise-result/combineWithAllErrors.ts

import { combineWithAllErrors, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const mixed = [asyncOk(1), asyncErr('a'), asyncErr('b')] as const;
const aggregated = combineWithAllErrors([...mixed]);
const errLabel = await matchAsync(
(vals: number[]) => vals.join(','),
(errs: string[]) => errs.join('|'),
aggregated,
);
// errLabel === 'a|b'
const clean = [asyncOk(1), asyncOk(2)] as const;
const allOk = await combineWithAllErrors([...clean]);
// allOk is Ok([1, 2]) when every input succeeds

Returns true if the Promise<IResultOfT> is success and contains the given value.

export function containsAsync<A>(
value: A,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<boolean>;
export function containsAsync<A, E>(
value: A,
r: Promise<IResultOfT<A, E>>,
): Promise<boolean>;

Defined in: promise-result/containsAsync.ts

import { containsAsync, unwrapOr } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const input = Promise.resolve(ok(42));
const same = await containsAsync(42, input);
const score = await unwrapOr(0, Promise.resolve(ok(same ? 1 : 0)));
// same === true and score === 1
const failed = Promise.resolve(err('missing'));
const onErr = await containsAsync(42, failed);
// onErr === false; containsAsync is false on any Err carrier

Returns true if the Promise<IResultOfT> is success and the predicate holds. Returns false on failure or when the predicate does not hold.

Throw policy: If the predicate throws synchronously or returns a rejected Promise, the rejection propagates to the outer Promise (matches the canonical AsyncResult throw policy — “sync throws and async rejections propagate”). Use existsAsyncOption if you want predicate errors to convert to false.

export function existsAsync<A>(
predicate: (a: A) => boolean | Promise<boolean>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<boolean>;
export function existsAsync<A, E>(
predicate: (a: A) => boolean | Promise<boolean>,
r: Promise<IResultOfT<A, E>>,
): Promise<boolean>;

Defined in: promise-result/existsAsync.ts

import { existsAsync, unwrapOr } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const input = Promise.resolve(ok(42));
const hits = await existsAsync(async (x: number) => x > 10, input);
const score = await unwrapOr(0, Promise.resolve(ok(hits ? 1 : 0)));
// hits === true and score === 1
const failed = Promise.resolve(err('skip'));
const onErr = await existsAsync(async (x: number) => x > 10, failed);
// onErr === false; existsAsync returns false when the carrier is Err

Filters the success value of a Promise<IResultOfT<A, E>> with a predicate. If the predicate holds, the original success passes through. If it fails, returns err(errorFn(value)). Failures pass through unchanged.

Throw policy: A synchronous throw or rejected Promise from predicate or errorFn propagates to the outer Promise (matches the canonical AsyncResult throw policy — “sync throws and async rejections propagate”). The thrown value is propagated as-is, not cast into the E channel; callers that need to capture thrown errors in the Err channel should wrap with tryCatch or similar.

export function filterOrElseAsync<A, E>(
predicate: (a: A) => boolean | Promise<boolean>,
errorFn: (a: A) => E | Promise<E>,
): (r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, E>>;
export function filterOrElseAsync<A, E>(
predicate: (a: A) => boolean | Promise<boolean>,
errorFn: (a: A) => E | Promise<E>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, E>>;

Defined in: promise-result/filterOrElseAsync.ts

import { filterOrElseAsync, matchAsync } from '@sandlada/result/promise-result';
import { ok } from '@sandlada/result/factories';
const input = Promise.resolve(ok(5));
const kept = filterOrElseAsync(
(x: number) => x > 0,
(x: number) => `${x} is not positive`,
input,
);
const okLabel = await matchAsync((n) => `ok:${n}`, (e) => e, kept);
// okLabel === 'ok:5'
const negative = Promise.resolve(ok(-3));
const rejected = await filterOrElseAsync(
(x: number) => x > 0,
(x: number) => `${x} is not positive`,
negative,
);
// rejected is Err('-3 is not positive'); predicate failure becomes Err via errorFn

Strictly synchronous flatten over a Promise<IResultOfT<IResultOfT>>. Unwraps exactly one layer.

export function flatten<A, E>(
r: Promise<IResultOfT<IResultOfT<A, E>, E>>,
): Promise<IResultOfT<A, E>>;

Defined in: promise-result/flatten.ts

import { flatten, matchAsync } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const nested = Promise.resolve(ok(ok(42)));
const flat = flatten(nested);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, flat);
// label === 'ok:42'
const innerErr = Promise.resolve(ok(err('x')));
const surfaced = await flatten(innerErr);
// surfaced is Err('x'); flatten passes through an inner failure

Flattens a nested Promise<IResultOfT<IResultOfT<A, E>, E>>.

Single-step only: unwraps exactly one layer. Call flattenAsync repeatedly to flatten deeper nests.

export function flattenAsync<A, E>(
r: Promise<IResultOfT<IResultOfT<A, E>, E>>,
): Promise<IResultOfT<A, E>>;

Defined in: promise-result/flattenAsync.ts

import { flattenAsync, matchAsync } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const nested = Promise.resolve(ok(ok(42)));
const flat = flattenAsync(nested);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, flat);
// label === 'ok:42'
const deeper = Promise.resolve(ok(ok(ok(7))));
const oneLayer = await flattenAsync(deeper);
// oneLayer is Ok(ok(7)); flattenAsync unwraps only one nesting level

Strictly synchronous map over a Promise<IResultOfT>. The mapper is required to be sync. Sync throws are caught and converted to err(caughtError). Async results from fn are not awaited.

For a callback that may be sync or async, prefer mapAsync.

export function map<A, B>(
f: (a: A) => B,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<B, E>>;
export function map<A, B, E>(
f: (a: A) => B,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<B, E>>;

Defined in: promise-result/map.ts

import { map, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(21);
const doubled = map((x: number) => x * 2, input);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, doubled);
// label === 'ok:42'
const boom = asyncErr('boom');
const passthrough = await map((x: number) => x * 2, boom);
// passthrough stays Err('boom'); map short-circuits on failure

Transforms the success value of a Promise<IResultOfT<A, E>>. The callback may be sync or async.

export function mapAsync<A, B>(
f: (a: A) => B | Promise<B>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<B, E>>;
export function mapAsync<A, B, E>(
f: (a: A) => B | Promise<B>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<B, E>>;

Defined in: promise-result/mapAsync.ts

import { mapAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(21);
const doubled = mapAsync((x: number) => x * 2, input);
const label = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, doubled);
// label === 'ok:42'
const skipped = asyncErr('skip');
const passthrough = await mapAsync((x: number) => x * 2, skipped);
// passthrough stays Err('skip'); mapAsync short-circuits on failure

Strictly synchronous mapErr over a Promise<IResultOfT>. The mapper is required to be sync. Sync throws are caught and converted to err(caughtError).

For a callback that may be sync or async, prefer mapErrAsync.

export function mapErr<A, E, F>(
f: (e: E) => F,
): <T>(r: Promise<IResultOfT<T, E>>) => Promise<IResultOfT<T, F>>;
export function mapErr<T, E, F>(
f: (e: E) => F,
r: Promise<IResultOfT<T, E>>,
): Promise<IResultOfT<T, F>>;

Defined in: promise-result/mapErr.ts

import { mapErr, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncErr('boom');
const wrapped = mapErr((e: string) => e.toUpperCase(), input);
const label = await matchAsync((n) => `ok:${n}`, (e) => e, wrapped);
// label === 'BOOM'
const healthy = asyncOk(42);
const passthrough = await mapErr((e: string) => e.toUpperCase(), healthy);
// passthrough stays Ok(42); mapErr short-circuits on success

Transforms the error of a Promise<IResultOfT<A, E>>.

export function mapErrAsync<E, F>(
f: (e: E) => F | Promise<F>,
): <A>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, F>>;
export function mapErrAsync<A, E, F>(
f: (e: E) => F | Promise<F>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, F>>;

Defined in: promise-result/mapErrAsync.ts

import { mapErrAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncErr('boom');
const wrapped = mapErrAsync(async (e: string) => `[wrapped] ${e}`, input);
const label = await matchAsync((n) => `ok:${n}`, (e) => e, wrapped);
// label === '[wrapped] boom'
const healthy = asyncOk(1);
const passthrough = await mapErrAsync(async (e: string) => e, healthy);
// passthrough stays Ok(1); mapErrAsync leaves success values untouched

Maps the success value of an async result, or returns defaultValue on failure. The mapping function may be sync or async. Equivalent to mapAsync(fn).then(unwrapOrAsync(defaultValue)) but more efficient.

Throw policy: if fn throws synchronously or its returned Promise<B> rejects, the result is defaultValue (not an Err). The thrown reason is discarded — use mapAsync(fn).then(unwrapOrElse(err)) if you need the reason.

The curried form accepts an optional onErr thunk that observes the rejected reason. Supplying onErr makes E inferable at the curried site, matching mapOrElseAsync’s inference behaviour. The error is still discarded — onErr exists purely for inference and side-effect observation, not for value substitution.

export function mapOrAsync<A, B, E>(
defaultValue: B,
fn: (a: A) => B | Promise<B>,
): <R extends Promise<IResultOfT<A, E>>>(r: R) => Promise<B>;
export function mapOrAsync<A, B, E>(
defaultValue: B,
fn: (a: A) => B | Promise<B>,
onErr: (e: E) => unknown,
): <R extends Promise<IResultOfT<A, E>>>(r: R) => Promise<B>;
export function mapOrAsync<A, B, E>(
defaultValue: B,
fn: (a: A) => B | Promise<B>,
r: Promise<IResultOfT<A, E>>,
): Promise<B>;
export function mapOrAsync<A, B, E>(
defaultValue: B,
fn: (a: A) => B | Promise<B>,
r: Promise<IResultOfT<A, E>>,
onErr: (e: E) => unknown,
): Promise<B>;

Defined in: promise-result/mapOrAsync.ts

import { mapOrAsync, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(5);
const mapped = await mapOrAsync(-1, (x: number) => x * 2, input);
const again = await mapAsync((x: number) => x + 1, asyncOk(mapped));
// mapped === 10 and again resolves to Ok(11)
const failed = asyncErr('boom');
const fallback = await mapOrAsync(-1, (x: number) => x * 2, failed);
// fallback === -1; mapOrAsync returns defaultValue on Err without calling fn

Maps the success value of an async result, or computes a default from the error on failure. Both callbacks may be sync or async. Equivalent to mapAsync(fn).then(unwrapOrElseAsync(onErr)) but more efficient.

export function mapOrElseAsync<A, B, E>(
onErr: (e: E) => B | Promise<B>,
fn: (a: A) => B | Promise<B>,
): (r: Promise<IResultOfT<A, E>>) => Promise<B>;
export function mapOrElseAsync<A, B, E>(
onErr: (e: E) => B | Promise<B>,
fn: (a: A) => B | Promise<B>,
r: Promise<IResultOfT<A, E>>,
): Promise<B>;

Defined in: promise-result/mapOrElseAsync.ts

import { mapOrElseAsync, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(5);
const mapped = await mapOrElseAsync((e: string) => 0, (x: number) => x * 2, input);
const again = await mapAsync((x: number) => x + 1, asyncOk(mapped));
// mapped === 10 and again resolves to Ok(11)
const failed = asyncErr('boom');
const fromErr = await mapOrElseAsync((e: string) => -e.length, (x: number) => x * 2, failed);
// fromErr === -4; onErr runs when the carrier is Err

Terminal — pattern-matches on both cases of an async result.

export function matchAsync<A, E, C>(
onOk: (a: A) => C | Promise<C>,
onErr: (e: E) => C | Promise<C>,
): (r: Promise<IResultOfT<A, E>>) => Promise<C>;
export function matchAsync<A, E, C>(
onOk: (a: A) => C | Promise<C>,
onErr: (e: E) => C | Promise<C>,
r: Promise<IResultOfT<A, E>>,
): Promise<C>;

Defined in: promise-result/matchAsync.ts

import { matchAsync, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(21);
const pipeline = mapAsync((x: number) => x * 2, input);
const okLabel = await matchAsync(
(v: number) => `success: ${v}`,
(e: string) => `failure: ${e}`,
pipeline,
);
// okLabel === 'success: 42'
const failed = asyncErr('boom');
const errLabel = await matchAsync(
(v: number) => `success: ${v}`,
(e: string) => `failure: ${e}`,
failed,
);
// errLabel === 'failure: boom'

Error recovery for async results. The success type widens to A | B.

Throw policy: a synchronous throw from f propagates via the outer promise rejection (the .then handler re-throws). A rejected Promise from f propagates as a rejection. Matches the canonical AsyncResult throw policy.

export function orElseAsync<E, B, F>(
f: (e: E) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
): <A>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A | B, F>>;
export function orElseAsync<A, E, B, F>(
f: (e: E) => IResultOfT<B, F> | Promise<IResultOfT<B, F>>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A | B, F>>;

Defined in: promise-result/orElseAsync.ts

import { orElseAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncErr('boom');
const recovered = orElseAsync((e: string) => asyncOk(`recovered:${e}`), input);
const label = await matchAsync((v) => v, (e) => String(e), recovered);
// label === 'recovered:boom'
const healthy = asyncOk(42);
const unchanged = await orElseAsync((e: string) => asyncOk(`recovered:${e}`), healthy);
// unchanged stays Ok(42); orElseAsync skips recovery on success

Swaps the success and failure variants of a Promise<IResultOfT<A, E>>.

export function swapAsync<A, E>(
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<E, A>>;

Defined in: promise-result/swapAsync.ts

import { swapAsync, matchAsync } from '@sandlada/result/promise-result';
import { ok, err } from '@sandlada/result/factories';
const wasOk = Promise.resolve(ok(5));
const flipped = swapAsync(wasOk);
const fromOk = await matchAsync((n) => `ok:${n}`, (e) => `err:${String(e)}`, flipped);
// flipped is Err(5); fromOk === 'err:5'
const wasErr = Promise.resolve(err('oops'));
const restored = await swapAsync(wasErr);
// restored is Ok('oops'); swapAsync moves errors into the success channel

Side-effect on the success track of an async result.

export function tapAsync<A>(
fn: (a: A) => void | Promise<void>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, E>>;
export function tapAsync<A, E>(
fn: (a: A) => void | Promise<void>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, E>>;

Defined in: promise-result/tapAsync.ts

import { tapAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
let seen = '';
const input = asyncOk('hello');
const afterTap = tapAsync((v: string) => { seen = v; }, input);
await afterTap;
const label = await matchAsync((s) => s, (e) => String(e), afterTap);
// seen === 'hello' and label === 'hello'
const broken = await tapAsync(() => { throw new Error('side'); }, asyncOk(1));
// broken is Err(Error('side')); tapAsync captures handler throws into Err

Side-effect on the failure track of an async result.

export function tapErrAsync<E>(
fn: (e: E) => void | Promise<void>,
): <A>(r: Promise<IResultOfT<A, E>>) => Promise<IResultOfT<A, E>>;
export function tapErrAsync<A, E>(
fn: (e: E) => void | Promise<void>,
r: Promise<IResultOfT<A, E>>,
): Promise<IResultOfT<A, E>>;

Defined in: promise-result/tapErrAsync.ts

import { tapErrAsync, matchAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
let logged = '';
const input = asyncErr('boom');
const afterTap = tapErrAsync((e: string) => { logged = e; }, input);
await afterTap;
const label = await matchAsync((v) => String(v), (e) => e, afterTap);
// logged === 'boom' and label === 'boom'
const healthy = asyncOk(1);
const skipped = await tapErrAsync((e: string) => { logged = e; }, healthy);
// skipped stays Ok(1); tapErrAsync ignores the success track

Strictly synchronous unwrapOr over a Promise<IResultOfT> — extracts the success value or returns a default. The default may itself be a Promise.

Note: returns Promise<A>, NOT Promise<IResultOfT<A, _>>. The Async suffix on unwrapOrAsync is preserved for naming parity with mapAsync/mapErrAsync, but both unwrap the inner value.

export function unwrapOr<A>(
defaultValue: A | Promise<A>,
): <E>(r: Promise<IResultOfT<A, E>>) => Promise<A>;
export function unwrapOr<A, E>(
defaultValue: A | Promise<A>,
r: Promise<IResultOfT<A, E>>,
): Promise<A>;

Defined in: promise-result/unwrapOr.ts

import { unwrapOr, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(42);
const value = await unwrapOr(0, input);
const doubled = await mapAsync((x: number) => x * 2, asyncOk(value));
// value === 42 and doubled resolves to Ok(84)
const failed = asyncErr('x');
const fallback = await unwrapOr(0, failed);
// fallback === 0; unwrapOr returns the default when the carrier is Err

Extracts the success value from Promise<IResultOfT>, or returns a default on failure. The default value may itself be a Promise<A>; it is awaited internally.

Returns Promise<A> (just the inner value, not wrapped). The previous Promise<IResultOfT<A, unknown>> signature was a bug — unwrapOr semantically unwraps.

The default value type D is independent of the success type A, so a wider or sentinel value can be supplied as a fallback — e.g. unwrapOrAsync<null>(null) for a Promise<IResultOfT<User, NetworkError>> resolves to Promise<User | null>.

export function unwrapOrAsync<A = unknown, D = A>(
defaultValue: D | Promise<D>,
): <A2 extends A, E>(r: Promise<IResultOfT<A2, E>>) => Promise<A2 | D>;
export function unwrapOrAsync<A, E, D = A>(
defaultValue: D | Promise<D>,
r: Promise<IResultOfT<A, E>>,
): Promise<A | D>;

Defined in: promise-result/unwrapOrAsync.ts

import { unwrapOrAsync, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(42);
const value = await unwrapOrAsync(0, input);
const doubled = await mapAsync((x: number) => x * 2, asyncOk(value));
// value === 42 and doubled resolves to Ok(84)
const failed = asyncErr('boom');
const fallback = await unwrapOrAsync(Promise.resolve(0), failed);
// fallback === 0; async defaults are awaited before returning

Strictly lazy unwrapOrElse over a Promise<IResultOfT> — extracts the success value or computes a default from the error via a thunk. Returns Promise<A> (just the value).

export function unwrapOrElse<A, E>(
onErr: (e: E) => A | Promise<A>,
): (r: Promise<IResultOfT<A, E>>) => Promise<A>;
export function unwrapOrElse<A, E>(
onErr: (e: E) => A | Promise<A>,
r: Promise<IResultOfT<A, E>>,
): Promise<A>;

Defined in: promise-result/unwrapOrElse.ts

import { unwrapOrElse, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(42);
const value = await unwrapOrElse((_e: string) => 0, input);
const doubled = await mapAsync((x: number) => x * 2, asyncOk(value));
// value === 42 and doubled resolves to Ok(84)
const failed = asyncErr('x');
const computed = await unwrapOrElse((e: string) => e.length, failed);
// computed === 1; unwrapOrElse calls the handler only on Err

Extracts the success value from Promise<IResultOfT>, or computes a default from the error on failure (lazy). The error handler may return a value or a Promise.

Returns Promise<A> (just the inner value).

The default value type D is independent of the success type A — the error handler may project to a different shape ((e) => null, (e) => defaultUser, etc.) and the result widens to A | D.

export function unwrapOrElseAsync<A = unknown, E = unknown, D = A>(
onErr: (e: E) => D | Promise<D>,
): <A2 extends A>(r: Promise<IResultOfT<A2, E>>) => Promise<A2 | D>;
export function unwrapOrElseAsync<A, E, D = A>(
onErr: (e: E) => D | Promise<D>,
r: Promise<IResultOfT<A, E>>,
): Promise<A | D>;

Defined in: promise-result/unwrapOrElseAsync.ts

import { unwrapOrElseAsync, mapAsync } from '@sandlada/result/promise-result';
import { asyncOk, asyncErr } from '@sandlada/result/factories';
const input = asyncOk(42);
const value = await unwrapOrElseAsync((_e: string) => Promise.resolve(0), input);
const doubled = await mapAsync((x: number) => x * 2, asyncOk(value));
// value === 42 and doubled resolves to Ok(84)
const failed = asyncErr('x');
const computed = await unwrapOrElseAsync((e: string) => Promise.resolve(e.length), failed);
// computed === 1; the async handler runs only when the carrier is Err