Skip to content

Combine Results: combine, all

Combine utilities — barrel export.

Re-exports result aggregation utilities for combining multiple Result values.

AllResult<T> = { isFailure: false; isSuccess: true; value: TupleValues<T>; } | { error: TupleErrors<T>; isFailure: true; isSuccess: false; }

Defined in: combine/all.ts:15

Public return shape - split so the failure branch honestly carries no value: the failure branch of the union has no value property at all, so the type cannot claim a success tuple it never constructed.

Type Parameter
T extends readonly IResultOfT<unknown, unknown>[]

Combines a tuple of results, preserving heterogeneous types. Returns the first failure (with no projected success tuple) or a success tuple.

Like Promise.all but for Result.

Cross-module divergence (intentional). combine/all accepts an empty tuple (readonly IResultOfT<unknown, unknown>[]) and returns Ok([]). The sibling option/all has the same two-overload shape: its tuple overload keeps the stricter readonly [IOption<unknown>, ...IOption<unknown>[]] constraint, but its array overload accepts a runtime-sized list - so option/all([]) typechecks there as well and returns Some([]). The combine/all constraint was relaxed so that all([]) typechecks; the option/all tuple constraint is left as-is because its tests pin the stricter contract. See combine/all.type-spec.ts for the regression test.

export function all<T extends readonly [IResultOfT<unknown, unknown>, ...IResultOfT<unknown, unknown>[]]>(
results: readonly [...T],
): AllResult<T>;
export function all<T extends readonly IResultOfT<unknown, unknown>[]>(
results: T,
): AllResult<T>;

Defined in: combine/all.ts

import { all } from '@sandlada/result/combine';
import { ok, err } from '@sandlada/result/factories';
import { match } from '@sandlada/result/operators';
const inputs = [ok(1), ok(2), ok(3)];
const combined = all(inputs);
const count = match(
(values) => values.length,
(error) => 0,
combined,
);
// count === 3
const withFailure = [ok(1), err('bad'), ok(3)];
const stopped = all(withFailure);
const code = match(
(values) => `ok:${values.length}`,
(error) => `err:${String(error)}`,
stopped,
);
// code === 'err:bad' — all short-circuits on the first Err (no success tuple)

Combines a tuple of results, preserving heterogeneous types. Returns the first failure (with no projected success tuple) or a success tuple.

Like Promise.all but for Result.

Cross-module divergence (intentional). combine/all accepts an empty tuple (readonly IResultOfT<unknown, unknown>[]) and returns Ok([]). The sibling option/all has the same two-overload shape: its tuple overload keeps the stricter readonly [IOption<unknown>, ...IOption<unknown>[]] constraint, but its array overload accepts a runtime-sized list - so option/all([]) typechecks there as well and returns Some([]). The combine/all constraint was relaxed so that all([]) typechecks; the option/all tuple constraint is left as-is because its tests pin the stricter contract. See combine/all.type-spec.ts for the regression test.

import { all } from '@sandlada/result/combine';
import { ok, err } from '@sandlada/result/factories';
import { match } from '@sandlada/result/operators';
const inputs = [ok(1), ok(2), ok(3)];
const combined = all(inputs);
const count = match(
(values) => values.length,
(error) => 0,
combined,
);
// count === 3
const withFailure = [ok(1), err('bad'), ok(3)];
const stopped = all(withFailure);
const code = match(
(values) => `ok:${values.length}`,
(error) => `err:${String(error)}`,
stopped,
);
// code === 'err:bad' — all short-circuits on the first Err (no success tuple)

Combines an array of results. Returns the first failure, or a success with all values.

Rust equivalent: Iterator::collect::<Result<Vec<_>, _>>()

Two overloads:

  • Tuple overload (readonly IResultOfT<unknown, unknown>[]): preserves per-position heterogeneous types — combine([ok(1), ok('hi')]) yields IResultOfT<[number, string], never>.
  • Homogeneous array overload (readonly IResultOfT<A, E>[]): collapses to IResultOfT<A[], E>. Useful when the input is a runtime-sized typed array that shares A and E.
export function combine<T extends readonly IResultOfT<unknown, unknown>[]>(
results: readonly [...T],
): IResultOfT<
{ [K in keyof T]: T[K] extends IResultOfT<infer V, unknown> ? V : never },
T[number] extends IResultOfT<unknown, infer E> ? E : never
>;
export function combine<A, E>(
results: readonly IResultOfT<A, E>[],
): IResultOfT<A[], E>;

Defined in: combine/combine.ts

import { combine } from '@sandlada/result/combine';
import { ok, err } from '@sandlada/result/factories';
import { map, match } from '@sandlada/result/operators';
import type { IResultOfT } from '@sandlada/result/types';
const hetero = [ok(1), ok('a')];
const heteroResult = combine(hetero);
const heteroLabel = match(
(values) => values.map(String).join('-'),
(error) => `fail:${String(error)}`,
heteroResult,
);
// heteroLabel === '1-a'
const homogeneous: IResultOfT<number, string>[] = [ok(1), ok(2)];
const homoResult = combine(homogeneous);
const summed = map((nums: number[]) => nums.reduce((a, b) => a + b, 0), homoResult);
const sumLabel = match(
(n) => `sum=${n}`,
(error) => `fail:${String(error)}`,
summed,
);
// sumLabel === 'sum=3'
const shortCircuit = combine([ok(1), err('fail'), ok(3)]);
const failFast = match(
(values) => `ok:${values.length}`,
(error) => `err:${String(error)}`,
shortCircuit,
);
// failFast === 'err:fail' — combine returns Err on the first failure

Combines results accumulating all errors (validation aggregation). Unlike combine (short-circuit on first failure), this collects every error.

Wlaschin equivalent: &&& (parallel AND in the ROP model)

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

Defined in: combine/combineWithAllErrors.ts

import { combineWithAllErrors } from '@sandlada/result/combine';
import { ok, err } from '@sandlada/result/factories';
import { match } from '@sandlada/result/operators';
const mixed = [ok(1), err('a'), err('b')];
const aggregated = combineWithAllErrors(mixed);
const errorList = match(
(values) => values.join(','),
(errors) => errors.join('|'),
aggregated,
);
// errorList === 'a|b' — all Err values are collected; success values are omitted
const allOk = [ok(1), ok(2)];
const combined = combineWithAllErrors(allOk);
const total = match(
(values) => values.reduce((a, b) => a + b, 0),
(errors) => errors.length,
combined,
);
// total === 3