Skip to content

Lazy AsyncResult operators

AsyncResult — barrel export.

Re-exports all AsyncResult factories and operators.

Returns res2 if res1 is Ok, otherwise returns the original Err. Short-circuiting — res2 is not evaluated when res1 is Err.

export function and<T, U, E>(
res1: AsyncResult<T, E>,
res2: AsyncResult<U, E>,
): AsyncResult<U, E>;

Defined in: async-result/and.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, and } from '@sandlada/result/async-result';
const r1 = await and(fromResult(ok(1)), fromResult(ok(2))).run(); // Ok(2)
const r2 = await and(fromResult(err<string>('a')), fromResult(ok(2))).run(); // Err('a')

Side-effect on success (sync or async), ignoring the callback’s result. Calls fn with the value on success and passes the original result through unchanged. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If the side-effect callback throws (or rejects), the result converts to err(caughtError) (canonical tap/tee policy — see AGENTS.md).

export function andTee<T, E>(
fn: (value: T) => void | unknown | Promise<void | unknown>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function andTee<T, E>(
fn: (value: T) => void | unknown | Promise<void | unknown>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/andTee.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, andTee } from '@sandlada/result/async-result';
const ar = andTee((v: number) => { console.log(v); }, fromResult(ok(42)));
const result = await ar.run(); // Ok(42)

Side-effect on success that can propagate errors. Calls fn with the value on success; if fn fails the failure widens into the original error type.

export function andThrough<T, E, F>(
fn: (value: T) => AsyncResult<unknown, F> | Promise<IResultOfT<unknown, F>>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E | F>;
export function andThrough<T, E, F>(
fn: (value: T) => AsyncResult<unknown, F> | Promise<IResultOfT<unknown, F>>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E | F>;

Defined in: async-result/andThrough.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, andThrough } from '@sandlada/result/async-result';
const validate = andThrough(
(v: number) => v > 0 ? fromResult(ok(undefined)) : Promise.reject(new Error('non-positive')),
fromResult(ok(42)),
);
const result = await validate.run(); // Ok(42)

AsyncResult applicative ap — applies a function wrapped in an AsyncResult to a value wrapped in an AsyncResult. If either 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. Matches the sync ap from @sandlada/result/operators.

Mirrors the sync ap for AsyncResult pipelines, matching the fp-ts Apply shape.

export function ap<A, B, E, F>(
fnResult: AsyncResult<(a: A) => B, E>,
): (result: AsyncResult<A, F>) => AsyncResult<B, E | F>;
export function ap<A, B, E, F>(
fnResult: AsyncResult<(a: A) => B, E>,
result: AsyncResult<A, F>,
): AsyncResult<B, E | F>;

Defined in: async-result/ap.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, ap } from '@sandlada/result/async-result';
const applied = await ap(fromResult(ok((x: number) => x * 2)), fromResult(ok(21))).run();
// Ok(42)

Simultaneously maps both variants of an AsyncResult.

Throw policy: If onOk or onErr throws (or rejects), the result converts to err(caughtError). Pass errorFn to customise how the thrown/rejected value maps onto your error union.

export function bimap<T, E, U, F>(
onOk: (value: T) => U | Promise<U>,
onErr: (error: E) => F | Promise<F>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<U, F>;
export function bimap<T, E, U, F>(
onOk: (value: T) => U | Promise<U>,
onErr: (error: E) => F | Promise<F>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => F,
): AsyncResult<U, F>;

Defined in: async-result/bimap.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, bimap } from '@sandlada/result/async-result';
const ar = bimap(
(v: number) => v.toString(),
(e: number) => e * 2,
fromResult(ok(5)),
);
const result = await ar.run(); // Ok('5')

Chains an AsyncResult-returning function on success (monadic bind / flatMap). Supports interoperability with standard Promise<IResultOfT>. Lazy — returns a new AsyncResult without executing the inner computation.

The outer AsyncResult’s error type and the inner callback’s returned AsyncResult error type are independent — the combined result widens to E | F. Mirrors the sync operators/bind.ts so heterogeneous-error pipelines (dbErr/validationErr) compose without manual unification.

Throw policy: If fn throws (sync) or rejects (async), the result converts to err(caughtError). Pass errorFn to customise how the thrown/rejected value maps onto your error union — e.g. bind(fn, thrown => new MyError(String(thrown))).

export function bind<T, U, E, F>(
fn: (value: T) => AsyncResult<U, F> | Promise<IResultOfT<U, F>>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<U, E | F>;
export function bind<T, U, E, F>(
fn: (value: T) => AsyncResult<U, F> | Promise<IResultOfT<U, F>>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E | F,
): AsyncResult<U, E | F>;

Defined in: async-result/bind.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, bind } from '@sandlada/result/async-result';
type AppError = { readonly kind: 'App' };
type DbError = { readonly kind: 'Db' };
// Heterogeneous errors - outer AppError, inner DbError, result widens.
const ar = bind<number, number, AppError, DbError>(
(id) => fromResult<number, DbError>(ok(id + 1)),
fromResult<number, AppError>(ok(1)),
);
// AsyncResult<number, AppError | DbError>

Async variant of catchErr for AsyncResult.

Recovers from an error by returning a fallback value T (or a Promise resolving to T), keeping the result track alive as a successful AsyncResult<T, never>.

Throw policy: failures inside onErr (sync throw or rejected Promise) are captured into Err(thrown), mirroring orElse / filterOrElse. A rejection of the source AsyncResult itself still propagates.

export function catchErr<A, E>(
onErr: (e: E) => A | Promise<A>,
): (ar: AsyncResult<A, E>) => AsyncResult<A, never>;
export function catchErr<A, E>(
onErr: (e: E) => A | Promise<A>,
ar: AsyncResult<A, E>,
): AsyncResult<A, never>;

Defined in: async-result/catchErr.ts

import { catchErr, fromResult } from '@sandlada/result/async-result';
import { err } from '@sandlada/result/factories';
const r = await catchErr((e: string) => 0, fromResult(err('boom'))).run();
// Ok(0)

Combines an array of AsyncResults into a single AsyncResult of an array. Short-circuits on the first failure (like Promise.all). Lazy — returns a new AsyncResult without executing the inner computations.

export function combine<T, E>(
results: readonly AsyncResult<T, E>[],
): AsyncResult<T[], E>;

Defined in: async-result/combine.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, combine } from '@sandlada/result/async-result';
const ar = combine([fromResult(ok(1)), fromResult(ok(2))]);
const result = await ar.run(); // Ok([1, 2])

Combines AsyncResults accumulating all errors (validation aggregation). Unlike combine (short-circuit on first failure), this collects every error. Lazy — returns a new AsyncResult without executing the inner computations.

export function combineWithAllErrors<T, E>(
results: readonly AsyncResult<T, E>[],
): AsyncResult<T[], E[]>;

Defined in: async-result/combineWithAllErrors.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, combineWithAllErrors } from '@sandlada/result/async-result';
const ar = combineWithAllErrors([
fromResult(ok(1)),
fromResult(err('a')),
fromResult(err('b')),
]);
const result = await ar.run(); // Err(['a', 'b'])

Returns a Promise<boolean> indicating if the AsyncResult is success and contains the given value.

export function contains<T>(
value: T,
): <E>(ar: AsyncResult<T, E>) => Promise<boolean>;
export function contains<T, E>(
value: T,
ar: AsyncResult<T, E>,
): Promise<boolean>;

Defined in: async-result/contains.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, contains } from '@sandlada/result/async-result';
const r = await contains(42, fromResult(ok(42))); // true

Returns true if the AsyncResult resolves to Err and contains the given value. Strict equality (===).

export function containsErr<T, E>(
error: E,
): (ar: AsyncResult<T, E>) => Promise<boolean>;
export function containsErr<T, E>(
error: E,
ar: AsyncResult<T, E>,
): Promise<boolean>;

Defined in: async-result/containsErr.ts

import { err } from '@sandlada/result/factories';
import { fromResult, containsErr } from '@sandlada/result/async-result';
await containsErr('boom', fromResult(err('boom'))); // true
await containsErr('nope', fromResult(err('boom'))); // false

Returns a Promise<boolean> indicating if the AsyncResult is success and the predicate holds.

export function exists<T>(
predicate: (value: T) => boolean | Promise<boolean>,
): <E>(ar: AsyncResult<T, E>) => Promise<boolean>;
export function exists<T, E>(
predicate: (value: T) => boolean | Promise<boolean>,
ar: AsyncResult<T, E>,
): Promise<boolean>;

Defined in: async-result/exists.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, exists } from '@sandlada/result/async-result';
const r = await exists((v: number) => v > 0, fromResult(ok(42))); // true

Like unwrap but throws an Error carrying the supplied message. Useful for marking program-contract violations with a domain-specific message.

The original error value is preserved as Error.cause (and via the optional formatErr hook for custom string shaping), so structured E shapes don’t get clobbered to [object Object].

Pass formatErr to customise how the original error value is rendered in the message; pass throwingFn to fully replace the thrown Error class — e.g. expect(msg, ar, info => new MyError(info.message, info.value)).

export function expect<T, E>(
message: string,
ar: AsyncResult<T, E>,
formatErr?: (error: E) => string,
): Promise<T>;
export function expect<T, E>(
message: string,
ar: AsyncResult<T, E>,
formatErr: ((error: E) => string) | undefined,
throwingFn: (info: { message: string; value: E }) => Error,
): Promise<T>;

Defined in: async-result/expect.ts

import { fromResult } from '@sandlada/result/async-result';
import { err } from '@sandlada/result/factories';
import { expect } from '@sandlada/result/async-result';
await expect('config must be valid', fromResult(err('boom'))); // throws Error
// The thrown Error carries `cause: 'boom'` so the original payload is preserved.

Like unwrapErr but throws an Error carrying the supplied message. Useful when reaching a success path is itself a contract violation.

The original success value is preserved as Error.cause (and via the optional throwingFn hook for full customisation), so structured T shapes don’t get lost.

export function expectErr<T, E>(
message: string,
ar: AsyncResult<T, E>,
throwingFn?: (info: { message: string; value: T }) => Error,
): Promise<E>;

Defined in: async-result/expectErr.ts

import { fromResult } from '@sandlada/result/async-result';
import { ok } from '@sandlada/result/factories';
import { expectErr } from '@sandlada/result/async-result';
await expectErr('should have failed', fromResult(ok(42))); // throws Error
// The thrown Error carries `cause: 42` so the original success value is preserved.

Filters the success value of an AsyncResult with a predicate. If the predicate holds, the original success passes through. If it fails, returns err(errorFn(value)). Failures pass through unchanged. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If the predicate or errorFn throws synchronously or returns a rejected Promise, the error is caught and the result converts to err(caughtError) (canonical catch+convert policy — see AGENTS.md).

export function filterOrElse<T, E>(
predicate: (value: T) => boolean | Promise<boolean>,
errorFn: (value: T) => E | Promise<E>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function filterOrElse<T, E>(
predicate: (value: T) => boolean | Promise<boolean>,
errorFn: (value: T) => E | Promise<E>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/filterOrElse.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, filterOrElse } from '@sandlada/result/async-result';
const ar = filterOrElse((x: number) => x > 0, (x: number) => `neg: ${x}`, fromResult(ok(42)));
const result = await ar.run(); // Ok(42)

Flattens a nested AsyncResult.

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

export function flatten<T, E>(
ar: AsyncResult<AsyncResult<T, E>, E>,
): AsyncResult<T, E>;

Defined in: async-result/flatten.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, flatten } from '@sandlada/result/async-result';
const nested = fromResult(ok(fromResult(ok(42))));
const ar = flatten(nested);
const result = await ar.run(); // Ok(42)

Creates an AsyncResult from a thunk that returns a Promise<IResultOfT>. The thunk is lazy — it won’t execute until .run() is called.

export function from<T, E = unknown>(
thunk: () => Promise<import('../types/IResultOfT.js').IResultOfT<T, E>>,
): AsyncResult<T, E>;

Defined in: async-result/from.ts

import type { AsyncResult } from '@sandlada/result';
import { from } from '@sandlada/result/async-result';
import { ok } from '@sandlada/result/factories';
const ar: AsyncResult<number, string> = from(() => Promise.resolve(ok(42)));
const result = await ar.run(); // IResultOfT<number, string>

Wraps a Promise<T> into an AsyncResult, catching rejections. The inner Promise is not yet created at construction time; the factory thunk is invoked lazily when .run() is called.

export function fromPromise<T, E = unknown>(
thunk: () => Promise<T>,
errorFn?: (error: unknown) => E,
): AsyncResult<T, E>;

Defined in: async-result/fromPromise.ts

import { fromPromise } from '@sandlada/result/async-result';
const ar = fromPromise(() => fetch('/api/data').then(r => r.json()));
const result = await ar.run();

Wraps a sync IResultOfT into an AsyncResult (lifts a sync Result into the async world). Equivalent to “asyncMap” — bridges sync Result to async transformation.

export function fromResult<T, E = unknown>(
result: IResultOfT<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/fromResult.ts

import { ok } from '@sandlada/result/factories';
import { fromResult } from '@sandlada/result/async-result';
const ar = fromResult(ok(42));
const result = await ar.run(); // IResultOfT<number, never>

Returns true if the AsyncResult resolves to Err. Mirrors the IResultOfT.isFailure discriminator as a standalone function.

export function isErr<T, E>(ar: AsyncResult<T, E>): Promise<boolean>;

Defined in: async-result/isErr.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, isErr } from '@sandlada/result/async-result';
await isErr(fromResult(ok(42))); // false
await isErr(fromResult(err('x'))); // true

Returns true if the AsyncResult resolves to Ok. Mirrors the IResultOfT.isSuccess discriminator as a standalone function.

export function isOk<T, E>(ar: AsyncResult<T, E>): Promise<boolean>;

Defined in: async-result/isOk.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, isOk } from '@sandlada/result/async-result';
await isOk(fromResult(ok(42))); // true
await isOk(fromResult(err('x'))); // false

Maps the success value of an AsyncResult using a synchronous function. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If fn throws, the result converts to err(caughtError). Pass errorFn to customise how the thrown value maps onto your error union.

export function map<T, U, E>(
fn: (value: T) => U,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<U, E>;
export function map<T, U, E>(
fn: (value: T) => U,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E,
): AsyncResult<U, E>;

Defined in: async-result/map.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, map } from '@sandlada/result/async-result';
const ar = map((x: number) => x * 2, fromResult(ok(21)));
const result = await ar.run(); // Ok(42)

Maps the success value of an AsyncResult. The callback may be sync or async (U | Promise<U>); sync results are awaited internally.

Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: a synchronous throw from fn propagates as a rejection of .run(); a rejected Promise from fn likewise propagates. The optional errorFn only remaps the rejection payload — it does NOT convert the rejection into an Err. The thrown value passed to errorFn is whatever .run() would otherwise reject with; the returned value replaces that rejection reason. This matches the canonical AsyncResult “propagates” policy. For catch-and-convert behaviour (where the rejection becomes Err(mapped)), use map (which has different throw semantics) or wrap the operation in a try/catch.

export function mapAsync<T, U, E>(
fn: (value: T) => U | Promise<U>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<U, E>;
export function mapAsync<T, U, E>(
fn: (value: T) => U | Promise<U>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E,
): AsyncResult<U, E>;

Defined in: async-result/mapAsync.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, mapAsync } from '@sandlada/result/async-result';
const ar = mapAsync((x: number) => x * 2, fromResult(ok(21)));
const result = await ar.run(); // Ok(42)

Maps the error of an AsyncResult using a synchronous function. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If fn throws, the result converts to err(caughtError). Pass errorFn to customise how the thrown value maps onto your error union.

export function mapErr<T, E, F>(
fn: (error: E) => F,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<T, F>;
export function mapErr<T, E, F>(
fn: (error: E) => F,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => F,
): AsyncResult<T, F>;

Defined in: async-result/mapErr.ts

import { err } from '@sandlada/result/factories';
import { fromResult, mapErr } from '@sandlada/result/async-result';
const ar = mapErr((e: string) => e.toUpperCase(), fromResult(err('oops')));
const result = await ar.run(); // Err('OOPS')

Maps the error of an AsyncResult using an async function. Lazy — returns a new AsyncResult without executing the inner computation.

export function mapErrAsync<T, E, F>(
fn: (error: E) => F | Promise<F>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, F>;
export function mapErrAsync<T, E, F>(
fn: (error: E) => F | Promise<F>,
ar: AsyncResult<T, E>,
): AsyncResult<T, F>;

Defined in: async-result/mapErrAsync.ts

import { err } from '@sandlada/result/factories';
import { fromResult, mapErrAsync } from '@sandlada/result/async-result';
const ar = mapErrAsync(async (e: string) => e.toUpperCase(), fromResult(err('oops')));
const result = await ar.run(); // Err('OOPS')

Maps the success value of an AsyncResult, or returns a default on failure. The mapper may be sync or async. Sync throws from the mapper are caught and converted to the default (canonical AsyncResult catch+convert policy).

export function mapOr<T, U, E>(
defaultValue: U,
fn: (value: T) => U | Promise<U>,
): (ar: AsyncResult<T, E>) => Promise<U>;
export function mapOr<T, U, E>(
defaultValue: U,
fn: (value: T) => U | Promise<U>,
ar: AsyncResult<T, E>,
): Promise<U>;

Defined in: async-result/mapOr.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, mapOr } from '@sandlada/result/async-result';
const v1 = await mapOr(-1, (x: number) => x * 2, fromResult(ok(21))); // 42
const v2 = await mapOr(-1, (x: number) => x * 2, fromResult(err('x'))); // -1

Maps the success value of an AsyncResult, or computes a default from the error on failure. Both callbacks may be sync or async. Lazy — onErr is only called on failure.

export function mapOrElse<T, U, E>(
onErr: (error: E) => U | Promise<U>,
fn: (value: T) => U | Promise<U>,
): (ar: AsyncResult<T, E>) => Promise<U>;
export function mapOrElse<T, U, E>(
onErr: (error: E) => U | Promise<U>,
fn: (value: T) => U | Promise<U>,
ar: AsyncResult<T, E>,
): Promise<U>;

Defined in: async-result/mapOrElse.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, mapOrElse } from '@sandlada/result/async-result';
const v1 = await mapOrElse((e: string) => -1, (x: number) => x * 2, fromResult(ok(21))); // 42
const v2 = await mapOrElse((e: string) => -1, (x: number) => x * 2, fromResult(err('x'))); // -1

Terminal operator — executes the AsyncResult and applies either the success handler or the error handler. Returns a Promise<U>.

export function match<T, E, U>(
handlers: { ok: (value: T) => U | Promise<U>; err: (error: E) => U | Promise<U> },
): (ar: AsyncResult<T, E>) => Promise<U>;
export function match<T, E, U>(
handlers: { ok: (value: T) => U | Promise<U>; err: (error: E) => U | Promise<U> },
ar: AsyncResult<T, E>,
): Promise<U>;

Defined in: async-result/match.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, match } from '@sandlada/result/async-result';
const result = await match({
ok: (x: number) => `got ${x}`,
err: (e: string) => `error: ${e}`,
}, fromResult(ok(42))); // 'got 42'

Returns res1 if it is Ok, otherwise returns res2. Short-circuiting — res2 is not evaluated when res1 is Ok.

export function or<T, E, F>(
res1: AsyncResult<T, E>,
res2: AsyncResult<T, F>,
): AsyncResult<T, E | F>;

Defined in: async-result/or.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, or } from '@sandlada/result/async-result';
const r1 = await or(fromResult(ok(1)), fromResult(ok(2))).run(); // Ok(1)
const r2 = await or(fromResult(err<string>('a')), fromResult(ok(2))).run(); // Ok(2)

Recovers from failure by chaining to an alternative AsyncResult or Promise<IResult>. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If fn throws (sync) or rejects (async), the result converts to err(caughtError). Pass errorFn to customise how the thrown value maps onto your error union.

export function orElse<T, E, F>(
fn: (error: E) => AsyncResult<T, F> | Promise<IResultOfT<T, F>>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E | F>;
export function orElse<T, E, F>(
fn: (error: E) => AsyncResult<T, F> | Promise<IResultOfT<T, F>>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E | F,
): AsyncResult<T, E | F>;

Defined in: async-result/orElse.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, orElse } from '@sandlada/result/async-result';
const ar = orElse((e: string) => fromResult(ok(0)), fromResult(err('fail')));
const result = await ar.run(); // Ok(0)

Side-effect on failure (sync or async), ignoring the callback’s result. Calls fn with the error on failure and passes the original result through unchanged. Lazy — returns a new AsyncResult without executing the inner computation.

Throw policy: If the side-effect callback throws (or rejects), the result converts to err(caughtError) (canonical tap/tee policy — see AGENTS.md).

export function orTee<T, E>(
fn: (error: E) => void | unknown | Promise<void | unknown>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function orTee<T, E>(
fn: (error: E) => void | unknown | Promise<void | unknown>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/orTee.ts

import { err } from '@sandlada/result/factories';
import { fromResult, orTee } from '@sandlada/result/async-result';
const ar = orTee((e: string) => { console.error(e); }, fromResult(err('oops')));
const result = await ar.run(); // Err('oops')

AsyncResult analogue of swap. Swaps the Ok and Err variants of an AsyncResult. Renamed from swap to align with mapAsync and other async-result operators.

export function swapAsync<T, E>(
ar: AsyncResult<T, E>,
): AsyncResult<E, T>;

Defined in: async-result/swapAsync.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, swapAsync } from '@sandlada/result/async-result';
const ar = swapAsync(fromResult(ok(5)));
const result = await ar.run(); // Err(5)

Side-effect on the success track. Calls fn with the value on success and passes the original result through unchanged. Lazy — returns a new AsyncResult without executing the inner computation.

The callback may be sync or async — fn is awaited internally so async rejections surface before the original result is returned.

Throw policy: If fn throws or rejects, the result converts to err(caughtError). Pass errorFn to customise how the thrown value maps onto your error union — e.g. tap(fn, thrown => new MyError(String(thrown))).

export function tap<T, E>(
fn: (value: T) => void | Promise<void>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function tap<T, E>(
fn: (value: T) => void | Promise<void>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E,
): AsyncResult<T, E>;

Defined in: async-result/tap.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, tap } from '@sandlada/result/async-result';
// Sync callback
const ar = tap((v: number) => console.log('got:', v), fromResult(ok(42)));
// Async callback — also awaited
const ar2 = tap(async (v: number) => { console.log(v); }, fromResult(ok(42)));

Side-effect on the success track using an async function. Lazy — returns a new AsyncResult without executing the inner computation.

export function tapAsync<T, E>(
fn: (value: T) => void | Promise<void>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function tapAsync<T, E>(
fn: (value: T) => void | Promise<void>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/tapAsync.ts

import { ok } from '@sandlada/result/factories';
import { fromResult, tapAsync } from '@sandlada/result/async-result';
const ar = tapAsync(async (v: number) => { console.log('saving', v); }, fromResult(ok(42)));

Side-effect on the error track. Calls fn with the error on failure and passes the original result through unchanged. Lazy — returns a new AsyncResult without executing the inner computation.

The callback may be sync or async — fn is awaited internally so async rejections surface before the original result is returned.

Throw policy: If fn throws or rejects, the result converts to err(caughtError). Pass errorFn to customise how the thrown value maps onto your error union.

export function tapErr<T, E>(
fn: (error: E) => void | Promise<void>,
errorFn?: (thrown: unknown) => unknown,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function tapErr<T, E>(
fn: (error: E) => void | Promise<void>,
ar: AsyncResult<T, E>,
errorFn?: (thrown: unknown) => E,
): AsyncResult<T, E>;

Defined in: async-result/tapErr.ts

import { err } from '@sandlada/result/factories';
import { fromResult, tapErr } from '@sandlada/result/async-result';
// Sync callback
const ar = tapErr((e: string) => console.log('err:', e), fromResult(err('oops')));
// Async callback — also supported
const ar2 = tapErr(async (e: string) => { console.log(e); }, fromResult(err('oops')));

Side-effect on the error track using an async function. Lazy — returns a new AsyncResult without executing the inner computation.

export function tapErrAsync<T, E>(
fn: (error: E) => void | Promise<void>,
): (ar: AsyncResult<T, E>) => AsyncResult<T, E>;
export function tapErrAsync<T, E>(
fn: (error: E) => void | Promise<void>,
ar: AsyncResult<T, E>,
): AsyncResult<T, E>;

Defined in: async-result/tapErrAsync.ts

import { err } from '@sandlada/result/factories';
import { fromResult, tapErrAsync } from '@sandlada/result/async-result';
const ar = tapErrAsync(async (e: string) => { console.log('err:', e); }, fromResult(err('oops')));

Extracts the success value from an AsyncResult, or throws on failure. Use sparingly — prefer unwrapOr, unwrapOrElse, or match in most code.

The original error value is preserved as Error.cause (or via the optional formatErr hook), so structured E shapes don’t get clobbered to [object Object]. Pass throwingFn to fully replace the thrown Error class.

export function unwrap<T, E>(ar: AsyncResult<T, E>): Promise<T>;
export function unwrap<T, E>(
ar: AsyncResult<T, E>,
formatErr?: (error: E) => string,
): Promise<T>;
export function unwrap<T, E>(
ar: AsyncResult<T, E>,
formatErr: ((error: E) => string) | undefined,
throwingFn: (info: { message: string; value: E }) => Error,
): Promise<T>;

Defined in: async-result/unwrap.ts

import { fromResult } from '@sandlada/result/async-result';
import { ok, err } from '@sandlada/result/factories';
import { unwrap } from '@sandlada/result/async-result';
await unwrap(fromResult(ok(42))); // 42
await unwrap(fromResult(err('boom'))); // throws Error with `cause: 'boom'`

Extracts the error from a failed AsyncResult, or throws on success. The dual of unwrap.

The original success value is preserved as Error.cause, so structured T shapes don’t get lost. Pass throwingFn to fully replace the thrown Error class.

export function unwrapErr<T, E>(ar: AsyncResult<T, E>): Promise<E>;
export function unwrapErr<T, E>(
ar: AsyncResult<T, E>,
throwingFn?: (info: { message: string; value: T }) => Error,
): Promise<E>;

Defined in: async-result/unwrapErr.ts

import { fromResult } from '@sandlada/result/async-result';
import { ok, err } from '@sandlada/result/factories';
import { unwrapErr } from '@sandlada/result/async-result';
await unwrapErr(fromResult(err('boom'))); // 'boom'
await unwrapErr(fromResult(ok(42))); // throws Error with `cause: 42`

Terminal operator — executes the AsyncResult and returns the success value, or a default value on failure. The default value may be sync or a Promise.

export function unwrapOr<T, E>(
defaultValue: T | Promise<T>,
): (ar: AsyncResult<T, E>) => Promise<T>;
export function unwrapOr<T, E>(
defaultValue: T | Promise<T>,
ar: AsyncResult<T, E>,
): Promise<T>;

Defined in: async-result/unwrapOr.ts

import { ok, err } from '@sandlada/result/factories';
import { fromResult, unwrapOr } from '@sandlada/result/async-result';
const result = await unwrapOr(0, fromResult(ok(42))); // 42
const fallback = await unwrapOr(0, fromResult(err('fail'))); // 0

Extracts the success value from an AsyncResult, or computes a default from the error on failure. The handler may be sync or async. Lazy — the handler is only called on failure.

export function unwrapOrElse<T, E, U>(
onErr: (error: E) => U | Promise<U>,
): (ar: AsyncResult<T, E>) => Promise<T | U>;
export function unwrapOrElse<T, E, U>(
onErr: (error: E) => U | Promise<U>,
ar: AsyncResult<T, E>,
): Promise<T | U>;

Defined in: async-result/unwrapOrElse.ts

import { fromResult } from '@sandlada/result/async-result';
import { ok, err } from '@sandlada/result/factories';
import { unwrapOrElse } from '@sandlada/result/async-result';
const v1 = await unwrapOrElse(() => 0, fromResult(ok(42))); // 42
const v2 = await unwrapOrElse((e: string) => -1, fromResult(err('boom'))); // -1