Skip to main content

Overview

Static methods on the Result namespace that combine lists of Results into a single Result. Similar to Promise.all(), but for Results. The combine variant short-circuits on the first error, while combineWithAllErrors collects all errors.

Result.combine()

Signature

// Heterogeneous lists (tuples)
Result.combine<T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]>(
  resultList: T
): CombineResults<T>

// Homogeneous lists (arrays)
Result.combine<T extends readonly Result<unknown, unknown>[]>(
  resultList: T
): CombineResults<T>

// Simplified types:
// Result.combine(Result<T, E>[]): Result<T[], E>
// Result.combine([Result<T1, E1>, Result<T2, E2>]): Result<[T1, T2], E1 | E2>

Parameters

resultList
Result<T, E>[] | [Result<T1, E1>, ...]
required
An array or tuple of Results to combine. Can be homogeneous (all same type) or heterogeneous (different types).

Returns

  • If all Results are Ok, returns Ok containing an array/tuple of all Ok values
  • If any Result is Err, returns the first Err encountered (short-circuits)
  • Preserves tuple types for heterogeneous lists

Result.combineWithAllErrors()

Signature

// Heterogeneous lists (tuples)
Result.combineWithAllErrors<T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]>(
  resultList: T
): CombineResultsWithAllErrorsArray<T>

// Homogeneous lists (arrays)  
Result.combineWithAllErrors<T extends readonly Result<unknown, unknown>[]>(
  resultList: T
): CombineResultsWithAllErrorsArray<T>

// Simplified types:
// Result.combineWithAllErrors(Result<T, E>[]): Result<T[], E[]>
// Result.combineWithAllErrors([Result<T1, E1>, Result<T2, E2>]): Result<[T1, T2], (E1 | E2)[]>

Parameters

resultList
Result<T, E>[] | [Result<T1, E1>, ...]
required
An array or tuple of Results to combine.

Returns

  • If all Results are Ok, returns Ok containing an array/tuple of all Ok values
  • If any Results are Err, returns Err containing an array of all errors
  • Does not short-circuit - evaluates all Results

Examples

Basic combine - Homogeneous List

import { ok, err, Result } from 'neverthrow'

const resultList: Result<number, never>[] = [
  ok(1),
  ok(2),
  ok(3)
]

const combined = Result.combine(resultList)
// combined is Result<number[], never>

combined.isOk() // true
combined._unsafeUnwrap() // [1, 2, 3]

combine with Error (Short-circuits)

const resultList: Result<number, string>[] = [
  ok(123),
  err('boooom!'),
  ok(456),
  err('ahhhhh!')  // This is never evaluated
]

const combined = Result.combine(resultList)

combined.isErr() // true
combined._unsafeUnwrapErr() // 'boooom!' (first error only)

Heterogeneous List (Tuple)

type HeterogeneousList = [
  Result<string, string>,
  Result<number, number>,
  Result<boolean, boolean>,
]

const heterogeneousList: HeterogeneousList = [
  ok('Yooooo'),
  ok(123),
  ok(true)
]

type ExpectedResult = Result<
  [string, number, boolean],
  string | number | boolean
>

const combined: ExpectedResult = Result.combine(heterogeneousList)

combined._unsafeUnwrap() // ['Yooooo', 123, true]

combineWithAllErrors - Collects All Errors

const resultList: Result<number, string>[] = [
  ok(123),
  err('boooom!'),
  ok(456),
  err('ahhhhh!')
]

const combined = Result.combineWithAllErrors(resultList)

combined.isErr() // true
combined._unsafeUnwrapErr() // ['boooom!', 'ahhhhh!'] (all errors)

Validation with All Errors

type ValidationError = { field: string; message: string }

function validateEmail(email: string): Result<string, ValidationError> {
  return email.includes('@')
    ? ok(email)
    : err({ field: 'email', message: 'Invalid email' })
}

function validatePassword(password: string): Result<string, ValidationError> {
  return password.length >= 8
    ? ok(password)
    : err({ field: 'password', message: 'Too short' })
}

function validateAge(age: number): Result<number, ValidationError> {
  return age >= 18
    ? ok(age)
    : err({ field: 'age', message: 'Must be 18+' })
}

const formValidation = Result.combineWithAllErrors([
  validateEmail('invalid'),
  validatePassword('short'),
  validateAge(16)
])

if (formValidation.isErr()) {
  const errors = formValidation.error
  // errors is ValidationError[]
  // [
  //   { field: 'email', message: 'Invalid email' },
  //   { field: 'password', message: 'Too short' },
  //   { field: 'age', message: 'Must be 18+' }
  // ]
}

Parallel Operations

function fetchUser(id: string): Result<User, string> {
  // ...
}

function fetchPosts(userId: string): Result<Post[], string> {
  // ...
}

function fetchComments(userId: string): Result<Comment[], string> {
  // ...
}

const userResult = fetchUser('123')
const postsResult = fetchPosts('123')
const commentsResult = fetchComments('123')

const allData = Result.combine([
  userResult,
  postsResult,
  commentsResult
])

allData.match(
  ([user, posts, comments]) => {
    // All data loaded successfully
    console.log(`${user.name} has ${posts.length} posts`)
  },
  (error) => {
    // At least one failed
    console.error('Failed to load data:', error)
  }
)

Array Processing

function parseNumber(str: string): Result<number, string> {
  const num = parseInt(str, 10)
  return isNaN(num) ? err(`Invalid: ${str}`) : ok(num)
}

const inputs = ['1', '2', '3', '4', '5']
const results = inputs.map(parseNumber)

const combined = Result.combine(results)
// combined is Result<number[], string>

combined.match(
  (numbers) => console.log('Sum:', numbers.reduce((a, b) => a + b)),
  (error) => console.error('Parse error:', error)
)

Type Preservation

// Arrays get concatenated into a flat array
const homogenousList = [
  ok(['hello', 'world']),
  ok([1, 2, 3])
]

type Expected = Result<[string[], number[]], unknown>

const result: Expected = Result.combine(homogenousList)

result._unsafeUnwrap()
// [['hello', 'world'], [1, 2, 3]]
// Arrays are NOT destructured

Real-World Multi-Step Validation

type Order = {
  items: Item[]
  payment: PaymentInfo
  shipping: ShippingAddress
}

function validateItems(items: Item[]): Result<Item[], string> {
  return items.length > 0
    ? ok(items)
    : err('Cart is empty')
}

function validatePayment(
  payment: PaymentInfo
): Result<PaymentInfo, string> {
  // ... validation
}

function validateShipping(
  address: ShippingAddress
): Result<ShippingAddress, string> {
  // ... validation
}

function processOrder(order: Order): Result<Order, string[]> {
  const validations = Result.combineWithAllErrors([
    validateItems(order.items),
    validatePayment(order.payment),
    validateShipping(order.shipping)
  ])

  return validations.map(([items, payment, shipping]) => ({
    items,
    payment,
    shipping
  }))
}

processOrder(order).match(
  (validOrder) => submitOrder(validOrder),
  (errors) => showErrors(errors) // Show all validation errors
)

With Different Error Types

type NetworkError = { type: 'network'; code: number }
type ParseError = { type: 'parse'; message: string }
type ValidationError = { type: 'validation'; field: string }

const results = [
  fetchData(),              // Result<Data, NetworkError>
  parseResponse(),          // Result<Parsed, ParseError>
  validateInput()           // Result<Valid, ValidationError>
]

const combined = Result.combineWithAllErrors(results)
// combined is Result<
//   [Data, Parsed, Valid],
//   (NetworkError | ParseError | ValidationError)[]
// >

Implementation Details

From the source code (result.ts:37-47):
export function combine<
  T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]
>(resultList: T): CombineResults<T> {
  return combineResultList(resultList) as CombineResults<T>
}

export function combineWithAllErrors<T extends readonly Result<unknown, unknown>[]>(
  resultList: T
): CombineResultsWithAllErrorsArray<T> {
  return combineResultListWithAllErrors(resultList) as CombineResultsWithAllErrorsArray<T>
}

When to Use Which?

Use combine when:

  • You want to fail fast on the first error
  • Errors are blocking (no point continuing)
  • You need best performance (short-circuits)
  • Similar to && operator behavior

Use combineWithAllErrors when:

  • You want to collect all errors
  • Useful for form validation (show all errors at once)
  • Non-blocking errors (all checks should run)
  • Better user experience (see all issues)

Build docs developers (and LLMs) love