Lazy AsyncOption operators
AsyncOption — barrel export.
Re-exports all AsyncOption factories and operators.
Functions
Section titled “Functions”Combines a tuple/array of AsyncOptions, preserving heterogeneous types.
Returns AsyncOption<None> if any element is None; otherwise
AsyncOption<Some<[v0, v1, ...]>>. Like Promise.all but with None
short-circuiting.
Two overloads:
- Tuple overload: preserves per-position heterogeneous types — yields
AsyncOption<Some<[number, string]>>. - Array overload: runtime-sized homogeneous arrays — yields
AsyncOption<Some<T[]>>.
export function all<T extends readonly [AsyncOption<unknown>, ...AsyncOption<unknown>[]]>( aos: T,): AsyncOption< { [K in keyof T]: T[K] extends AsyncOption<infer V> ? V : never }>;export function all<T>( aos: readonly AsyncOption<T>[],): AsyncOption<T[]>;Defined in: async-option/all.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { all, fromOption } from '@sandlada/result/async-option';
const left = fromOption(ofSome(1));const right = fromOption(ofSome(2));const combined = all([left, right]);const pair = await combined.run();// pair is Some([1, 2])
const broken = all([fromOption(ofSome(1)), fromOption(ofNone<number>())]);const none = await broken.run();// none is None; all short-circuits when any input is Nonebind()
Section titled “bind()”Chains an AsyncOption-returning function on success (monadic bind / flatMap).
Supports interoperability by also accepting a function that returns Promise<IOption<U>>.
Lazy — returns a new AsyncOption without executing the inner computation.
export function bind<T, U>( fn: (value: T) => AsyncOption<U> | Promise<IOption<U>>,): (ao: AsyncOption<T>) => AsyncOption<U>;export function bind<T, U>( fn: (value: T) => AsyncOption<U> | Promise<IOption<U>>, ao: AsyncOption<T>,): AsyncOption<U>;Defined in: async-option/bind.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { bind, fromOption, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const chained = bind((x: number) => fromOption(ofSome(x * 2)), input);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, chained,);// label === 'value:42'
const empty = fromOption(ofNone<number>());const skipped = bind((x: number) => fromOption(ofSome(x * 2)), empty);const absent = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, skipped,);// absent === 'missing'; bind short-circuits on Nonecontains()
Section titled “contains()”Returns a Promise<boolean> indicating if the AsyncOption is Some and contains the given value.
export function contains<T>( value: T,): (ao: AsyncOption<T>) => Promise<boolean>;export function contains<T>( value: T, ao: AsyncOption<T>,): Promise<boolean>;Defined in: async-option/contains.ts
exists()
Section titled “exists()”Returns a Promise<boolean> indicating if the AsyncOption is Some and the predicate holds.
Throw policy: a predicate that throws synchronously or rejects is
collapsed to false - the same contract the eager sibling
promise-option/existsAsyncOption has, and the convention of this module,
where every callback captures its failure instead of propagating it.
export function exists<T>( predicate: (value: T) => boolean | Promise<boolean>,): (ao: AsyncOption<T>) => Promise<boolean>;export function exists<T>( predicate: (value: T) => boolean | Promise<boolean>, ao: AsyncOption<T>,): Promise<boolean>;Defined in: async-option/exists.ts
filter()
Section titled “filter()”Filters the value of an AsyncOption with a predicate. Keeps the Some if the predicate holds; converts to None otherwise. None passes through. Lazy — returns a new AsyncOption without executing the inner computation.
Throw policy: If the predicate throws synchronously or returns a rejected
Promise, the error is caught and the result converts to None
(canonical catch+convert policy — see AGENTS.md).
export function filter<T>( predicate: (value: T) => boolean | Promise<boolean>,): (ao: AsyncOption<T>) => AsyncOption<T>;export function filter<T>( predicate: (value: T) => boolean | Promise<boolean>, ao: AsyncOption<T>,): AsyncOption<T>;Defined in: async-option/filter.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { filter, fromOption, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const kept = filter((x: number) => x > 10, input);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, kept,);// label === 'value:21'
const narrow = filter((x: number) => x > 100, input);const rejected = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, narrow,);// rejected === 'missing'; filter drops failing predicates to None
const empty = fromOption(ofNone<number>());const skipped = filter((x: number) => x > 10, empty);const absent = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, skipped,);// absent === 'missing'; filter passes None throughflatten()
Section titled “flatten()”Flattens a nested AsyncOption.
Single-step only: unwraps exactly one layer. Call flatten repeatedly
to flatten deeper nests.
export function flatten<T>( ao: AsyncOption<AsyncOption<T>>,): AsyncOption<T>;Defined in: async-option/flatten.ts
from()
Section titled “from()”Creates an AsyncOption from a thunk that returns an Option or a
Promise<Option>. The thunk is lazy - it won’t execute until .run() is
called.
The carrier’s run() always returns a Promise: a thunk that returns a plain
Option is awaited, and a thunk that throws synchronously surfaces as a
rejected promise instead of escaping as a raw exception.
export function from<T>( thunk: () => IOption<T> | Promise<IOption<T>>,): AsyncOption<T>;Defined in: async-option/from.ts
Example
Section titled “Example”import { ofSome } from '@sandlada/result/option';import { from, map } from '@sandlada/result/async-option';
const input = () => Promise.resolve(ofSome(21));const ao = map((x: number) => x * 2, from(input));const result = await ao.run();// result is Some(42); from defers the thunk until run()fromOption()
Section titled “fromOption()”Wraps a sync IOption into an AsyncOption (lifts a sync Option into the async world).
export function fromOption<T>( option: IOption<T>,): AsyncOption<T>;Defined in: async-option/fromOption.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match } from '@sandlada/result/async-option';
const input = ofSome(42);const ao = fromOption(input);const label = await match( { some: (n) => `some:${n}`, none: () => 'none' }, ao,);// label === 'some:42'
const missing = fromOption(ofNone<number>());const absent = await match( { some: (n) => `some:${n}`, none: () => 'none' }, missing,);// absent === 'none'fromPromise()
Section titled “fromPromise()”Wraps a Promise<T> into an AsyncOption, catching rejections.
If the promise resolves, it returns Some(value).
If it rejects, it returns None.
The inner Promise is not yet created at construction time; the factory thunk is invoked
lazily when .run() is called.
export function fromPromise<T>( thunk: () => Promise<T>,): AsyncOption<T>;Defined in: async-option/fromPromise.ts
Example
Section titled “Example”import { fromPromise, match } from '@sandlada/result/async-option';
const input = () => Promise.resolve(99);const ao = fromPromise(input);const label = await match( { some: (n) => `got:${n}`, none: () => 'missing' }, ao,);// label === 'got:99'
const rejected = fromPromise(() => Promise.reject(new Error('fail')));const absent = await match( { some: () => 'ok', none: () => 'missing' }, rejected,);// absent === 'missing'; fromPromise turns rejections into NoneisNone()
Section titled “isNone()”Returns true if the AsyncOption resolves to None. Mirrors the
IOption.isNone discriminator as a standalone function.
export function isNone<T>(ao: AsyncOption<T>): Promise<boolean>;Defined in: async-option/isNone.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, isNone, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const no = await isNone(input);// no === falseconst label = await match( { some: () => 'some', none: () => 'none' }, input,);// label === 'some'
const empty = fromOption(ofNone<number>());const yes = await isNone(empty);// yes === trueisSome()
Section titled “isSome()”Returns true if the AsyncOption resolves to Some. Mirrors the
IOption.isSome discriminator as a standalone function.
export function isSome<T>(ao: AsyncOption<T>): Promise<boolean>;Defined in: async-option/isSome.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, isSome, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const yes = await isSome(input);// yes === trueconst label = await match( { some: () => 'some', none: () => 'none' }, input,);// label === 'some'
const empty = fromOption(ofNone<number>());const no = await isSome(empty);// no === falseMaps the value of an AsyncOption using a synchronous function. Lazy — returns a new AsyncOption without executing the inner computation.
export function map<T, U>( fn: (value: T) => U,): (ao: AsyncOption<T>) => AsyncOption<U>;export function map<T, U>( fn: (value: T) => U, ao: AsyncOption<T>,): AsyncOption<U>;Defined in: async-option/map.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, map, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const mapped = map((x: number) => x * 2, input);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, mapped,);// label === 'value:42'
const empty = fromOption(ofNone<number>());const skipped = map((x: number) => x * 2, empty);const absent = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, skipped,);// absent === 'missing'; map passes None throughmapAsync()
Section titled “mapAsync()”Maps the value of an AsyncOption using an async function. Lazy — returns a new AsyncOption without executing the inner computation.
export function mapAsync<T, U>( fn: (value: T) => Promise<U>,): (ao: AsyncOption<T>) => AsyncOption<U>;export function mapAsync<T, U>( fn: (value: T) => Promise<U>, ao: AsyncOption<T>,): AsyncOption<U>;Defined in: async-option/mapAsync.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, mapAsync, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const mapped = mapAsync(async (x: number) => x * 2, input);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, mapped,);// label === 'value:42'
const empty = fromOption(ofNone<number>());const skipped = mapAsync(async (x: number) => x * 2, empty);const absent = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, skipped,);// absent === 'missing'; mapAsync passes None throughmapOr()
Section titled “mapOr()”Maps the value of an AsyncOption, returning a default on None.
The mapper may be sync or async. Throws from the mapper are caught and
converted to the default (canonical AsyncOption catch+convert policy).
export function mapOr<T, U>( defaultValue: U, fn: (value: T) => U | Promise<U>,): (ao: AsyncOption<T>) => Promise<U>;export function mapOr<T, U>( defaultValue: U, fn: (value: T) => U | Promise<U>, ao: AsyncOption<T>,): Promise<U>;Defined in: async-option/mapOr.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, mapOr } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const mapped = await mapOr(-1, (x: number) => x * 2, input);// mapped === 42
const empty = fromOption(ofNone<number>());const fallback = await mapOr(-1, (x: number) => x * 2, empty);// fallback === -1; mapOr uses the default on NonemapOrElse()
Section titled “mapOrElse()”Maps the value of an AsyncOption, or computes a default from a thunk on None.
Both callbacks may be sync or async.
export function mapOrElse<T, U>( onNone: () => U | Promise<U>, fn: (value: T) => U | Promise<U>,): (ao: AsyncOption<T>) => Promise<U>;export function mapOrElse<T, U>( onNone: () => U | Promise<U>, fn: (value: T) => U | Promise<U>, ao: AsyncOption<T>,): Promise<U>;Defined in: async-option/mapOrElse.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, mapOrElse } from '@sandlada/result/async-option';
const input = fromOption(ofSome(21));const mapped = await mapOrElse(() => -1, (x: number) => x * 2, input);// mapped === 42
const empty = fromOption(ofNone<number>());const fallback = await mapOrElse(() => -1, (x: number) => x * 2, empty);// fallback === -1; mapOrElse runs onNone when input is Nonematch()
Section titled “match()”Terminal — pattern-matches on both cases of an AsyncOption.
export function match<T, U>( handlers: { some: (value: T) => U | Promise<U>; none: () => U | Promise<U> },): (ao: AsyncOption<T>) => Promise<U>;export function match<T, U>( handlers: { some: (value: T) => U | Promise<U>; none: () => U | Promise<U> }, ao: AsyncOption<T>,): Promise<U>;Defined in: async-option/match.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const label = await match( { some: (v: number) => `success: ${v}`, none: () => 'failure' }, input,);// label === 'success: 42'
const empty = fromOption(ofNone<number>());const absent = await match( { some: (v: number) => `success: ${v}`, none: () => 'failure' }, empty,);// absent === 'failure'ofNone()
Section titled “ofNone()”Creates an AsyncOption that always resolves to None.
Equivalent to fromOption(ofNone()) but skips the sync intermediate.
Default T = unknown mirrors option/ofNone so contextual typing flows
the same way (const x: AsyncOption<number> = ofNone() widens without
needing an explicit generic argument).
export function ofNone<T = unknown>(): AsyncOption<T>;Defined in: async-option/ofNone.ts
Example
Section titled “Example”import { ofSome } from '@sandlada/result/option';import { fromOption, match, ofNone, orElse } from '@sandlada/result/async-option';
const input = ofNone<number>();const recovered = orElse(() => fromOption(ofSome(0)), input);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, recovered,);// label === 'value:0'; orElse supplies Some when input is NoneofSome()
Section titled “ofSome()”Lifts a raw value into an AsyncOption<T> that resolves to Some(value).
Equivalent to fromOption(ofSome(value)) but skips the sync intermediate.
export function ofSome<T>(value: T): AsyncOption<T>;Defined in: async-option/ofSome.ts
Example
Section titled “Example”import { map, match, ofSome } from '@sandlada/result/async-option';
const input = 21;const ao = map((x: number) => x * 2, ofSome(input));const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, ao,);// label === 'value:42'okOr()
Section titled “okOr()”Converts an AsyncOption<T> into an AsyncResult<T, E>, supplying an error
value for the None case.
export function okOr<T, E>( error: E,): (ao: AsyncOption<T>) => AsyncResult<T, E>;export function okOr<T, E>( error: E, ao: AsyncOption<T>,): AsyncResult<T, E>;Defined in: async-option/okOr.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, okOr } from '@sandlada/result/async-option';import { match } from '@sandlada/result/async-result';
const input = fromOption(ofSome(42));const ar = okOr('missing', input);const okLabel = await match( { ok: (v: number) => `ok:${v}`, err: (e: string) => `err:${e}` }, ar,);// okLabel === 'ok:42'
const empty = fromOption(ofNone<number>());const failed = okOr('missing', empty);const errLabel = await match( { ok: (v: number) => `ok:${v}`, err: (e: string) => `err:${e}` }, failed,);// errLabel === 'err:missing'okOrElse()
Section titled “okOrElse()”Converts an AsyncOption<T> into an AsyncResult<T, E | Error>, computing the
error from a thunk on None (lazy - error is only built when needed).
Throw policy: a thunk that throws synchronously or rejects is captured
as the result error. Because the caught value can be any unknown, the
return type is widened to E | Error - the runtime error may be an Error
instance even when the user-declared E is something else. A caught value
that is not an Error instance is wrapped in one (the original value stays
available through cause), so the declared error type holds for every value
the caller can receive.
export function okOrElse<T, E>( onNone: () => E | Promise<E>,): (ao: AsyncOption<T>) => AsyncResult<T, E | Error>;export function okOrElse<T, E>( onNone: () => E | Promise<E>, ao: AsyncOption<T>,): AsyncResult<T, E | Error>;Defined in: async-option/okOrElse.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, okOrElse } from '@sandlada/result/async-option';import { match } from '@sandlada/result/async-result';
const input = fromOption(ofSome(42));const ar = okOrElse(() => 'missing', input);const okLabel = await match( { ok: (v: number) => `ok:${v}`, err: (e: string | Error) => `err:${String(e)}` }, ar,);// okLabel === 'ok:42'
const empty = fromOption(ofNone<number>());const failed = okOrElse(() => 'missing', empty);const errLabel = await match( { ok: (v: number) => `ok:${v}`, err: (e: string | Error) => `err:${String(e)}` }, failed,);// errLabel === 'err:missing'orElse()
Section titled “orElse()”Curried form. The inner <T> is deferred so the AsyncOption’s value
type is re-inferred at every application site.
Recovers from None by chaining to an alternative AsyncOption or Promise<IOption>. Lazy — returns a new AsyncOption without executing the inner computation.
The fallback’s value type (U) is independent of the input’s value type (T),
so a recovery can return a structurally broader or different value — the
resulting carrier is AsyncOption<T | U>. Pattern-matching on the result narrows
to T on the original Some and to U on the recovered branch.
export function orElse<U>( fn: () => AsyncOption<U> | Promise<IOption<U>>,): <T>(ao: AsyncOption<T>) => AsyncOption<T | U>;export function orElse<T, U>( fn: () => AsyncOption<U> | Promise<IOption<U>>, ao: AsyncOption<T>,): AsyncOption<T | U>;Defined in: async-option/orElse.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match, orElse } from '@sandlada/result/async-option';
const empty = fromOption(ofNone<number>());const recovered = orElse(() => fromOption(ofSome(0)), empty);const label = await match( { some: (n) => `value:${n}`, none: () => 'missing' }, recovered,);// label === 'value:0'
const present = fromOption(ofSome(5));const kept = orElse(() => fromOption(ofSome(0)), present);const same = await kept.run();// same is Some(5); orElse keeps the original SomeDirect form. T is inferred from the supplied AsyncOption; the result widens to T | U.
Side-effect on the success track of an AsyncOption. Calls fn
with the value on Some and passes the original Option through unchanged.
Lazy — returns a new AsyncOption without executing the inner computation.
Throw policy: If the side-effect callback throws, the result converts
to None (canonical tap/tee policy — see AGENTS.md).
export function tap<T>( fn: (value: T) => void,): (ao: AsyncOption<T>) => AsyncOption<T>;export function tap<T>( fn: (value: T) => void, ao: AsyncOption<T>,): AsyncOption<T>;Defined in: async-option/tap.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match, tap } from '@sandlada/result/async-option';
let seen = 0;const input = fromOption(ofSome(42));const tapped = tap((v: number) => { seen = v; }, input);const kept = await tapped.run();// kept is Some(42) and seen === 42
const empty = fromOption(ofNone<number>());const skipped = tap((v: number) => { seen = v; }, empty);const absent = await match( { some: () => 'some', none: () => 'none' }, skipped,);// absent === 'none'; tap passes None through without calling the side effecttapAsync()
Section titled “tapAsync()”Side-effect on the success track of an AsyncOption using an async function.
Calls fn with the value on Some and passes the original Option through unchanged.
Lazy — returns a new AsyncOption without executing the inner computation.
Throw policy: If the side-effect callback throws (or rejects), the result
converts to None (canonical tap/tee policy — see AGENTS.md).
export function tapAsync<T>( fn: (value: T) => void | Promise<void>,): (ao: AsyncOption<T>) => AsyncOption<T>;export function tapAsync<T>( fn: (value: T) => void | Promise<void>, ao: AsyncOption<T>,): AsyncOption<T>;Defined in: async-option/tapAsync.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match, tapAsync } from '@sandlada/result/async-option';
let saved = false;const input = fromOption(ofSome(42));const tapped = tapAsync(async (v: number) => { saved = v === 42; }, input);const kept = await tapped.run();// kept is Some(42) and saved === true
const empty = fromOption(ofNone<number>());const skipped = tapAsync(async () => { saved = true; }, empty);const absent = await match( { some: () => 'some', none: () => 'none' }, skipped,);// absent === 'none'; tapAsync skips the side effect on Nonetranspose()
Section titled “transpose()”Transposes an AsyncOption<AsyncResult<T, E>> into an
AsyncResult<AsyncOption<T>, E>.
Some(Ok(v))→Ok(Some(v))Some(Err(e))→Err(e)None→Ok(None)
export function transpose<T, E>( ao: AsyncOption<AsyncResult<T, E>>,): AsyncResult<AsyncOption<T>, E>;Defined in: async-option/transpose.ts
Example
Section titled “Example”import { ofNone, ofSome, transpose } from '@sandlada/result/async-option';import { fromResult, match } from '@sandlada/result/async-result';import { ok } from '@sandlada/result/factories';import type { AsyncResult } from '@sandlada/result/types';
const input = ofSome(fromResult(ok(42)));const ar = transpose(input);const label = await match( { ok: async (nested) => { const opt = await nested.run(); return opt.isSome ? `some:${opt.value}` : 'none'; }, err: (e: string) => `err:${e}`, }, ar,);// label === 'some:42'
const empty = ofNone<AsyncResult<number, string>>();const whenNone = await match( { ok: async (nested) => { const opt = await nested.run(); return opt.isSome ? 'some' : 'none'; }, err: (e: string) => `err:${e}`, }, transpose(empty),);// whenNone === 'none'; outer None becomes Ok carrying a None AsyncOptionunwrap()
Section titled “unwrap()”Extracts the value from an AsyncOption, or throws if None.
Use sparingly — prefer unwrapOr, unwrapOrElse, or match in most code.
export function unwrap<T>(ao: AsyncOption<T>): Promise<T>;Defined in: async-option/unwrap.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, isNone, unwrap } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const value = await unwrap(input);// value === 42
const empty = fromOption(ofNone<number>());const missing = await isNone(empty);// missing === true; unwrap(empty) would throw on NoneunwrapOr()
Section titled “unwrapOr()”Extracts the value from an AsyncOption, or returns a default value.
export function unwrapOr<T>( defaultValue: T | Promise<T>,): (ao: AsyncOption<T>) => Promise<T>;export function unwrapOr<T>( defaultValue: T | Promise<T>, ao: AsyncOption<T>,): Promise<T>;Defined in: async-option/unwrapOr.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, unwrapOr } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const value = await unwrapOr(0, input);// value === 42
const empty = fromOption(ofNone<number>());const fallback = await unwrapOr(0, empty);// fallback === 0; unwrapOr returns the default on NoneunwrapOrElse()
Section titled “unwrapOrElse()”Extracts the value from an AsyncOption, or computes a default from a thunk
on None. Lazy — the default is only computed when needed.
export function unwrapOrElse<T>( onNone: () => T | Promise<T>,): (ao: AsyncOption<T>) => Promise<T>;export function unwrapOrElse<T>( onNone: () => T | Promise<T>, ao: AsyncOption<T>,): Promise<T>;Defined in: async-option/unwrapOrElse.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, unwrapOrElse } from '@sandlada/result/async-option';
const input = fromOption(ofSome(42));const value = await unwrapOrElse(() => 0, input);// value === 42
const empty = fromOption(ofNone<number>());const fallback = await unwrapOrElse(() => 0, empty);// fallback === 0; unwrapOrElse runs onNone when input is NonezipWith()
Section titled “zipWith()”Combines N AsyncOptions (N ≥ 2) with a function. If all
resolve to Some, returns AsyncOption<Some(fn(a, b, ...))>. If any
resolves to None, returns AsyncOption<None>. A synchronous throw from
the callback collapses to None; async rejections from the callback
propagate (they are not caught).
The arity of fn fixes the number of AsyncOptions accepted — variadic via
tuple inference. The catch-all mapped-type variadic handles every arity ≥ 2
in a single pair of overloads.
Design note (type safety): This file intentionally does NOT declare
per-arity overloads. TypeScript’s function-type bivariance would let a
2-argument fn match a 3-argument overload (with the third parameter
silently ignored), and a 0-argument fn match a 2-argument curried form.
The single variadic below avoids both holes: T is inferred from fn’s
actual parameter list, and the constraint readonly [unknown, unknown, ...unknown[]] requires ≥ 2 elements.
export function zipWith<T extends readonly [unknown, unknown, ...unknown[]], R>( fn: (...args: T) => R | Promise<R>,): (...aos: { [K in keyof T]: AsyncOption<T[K]> }) => AsyncOption<R>;export function zipWith<T extends readonly [unknown, unknown, ...unknown[]], R>( fn: (...args: T) => R | Promise<R>, ...aos: { [K in keyof T]: AsyncOption<T[K]> }): AsyncOption<R>;Defined in: async-option/zipWith.ts
Example
Section titled “Example”import { ofSome, ofNone } from '@sandlada/result/option';import { fromOption, match, zipWith } from '@sandlada/result/async-option';
const left = fromOption(ofSome(1));const right = fromOption(ofSome(2));const summed = zipWith((a: number, b: number) => a + b, left, right);const label = await match( { some: (n) => `sum:${n}`, none: () => 'missing' }, summed,);// label === 'sum:3'
const broken = zipWith( (a: number, b: number) => a + b, left, fromOption(ofNone<number>()),);const absent = await match( { some: (n) => `sum:${n}`, none: () => 'missing' }, broken,);// absent === 'missing'; zipWith short-circuits when any input is None