Skip to main content

Overview

Takes an Err value and maps it to a Result<T, SomeNewType>. This is the error-handling counterpart to andThen, useful for error recovery and fallback logic.

Signature

class Result<T, E> {
  orElse<R extends Result<unknown, unknown>>(
    f: (e: E) => R
  ): Result<InferOkTypes<R> | T, InferErrTypes<R>>
  
  orElse<U, A>(f: (e: E) => Result<U, A>): Result<U | T, A>
}

Parameters

f
(e: E) => Result<U, A>
required
A function that takes the Err value and returns a new Result. This function is only called if the current Result is Err.

Returns

Returns a new Result<T | U, A> where:
  • If the original Result is Ok(value), returns Ok(value) without calling f
  • If the original Result is Err(error), returns the result of f(error)
  • The success type becomes a union of both possible success types

Examples

Basic Error Recovery

import { ok, err } from 'neverthrow'

const okVal = ok(12)
const errorCallback = (errVal: string) => 
  err<number, boolean>(true)

okVal.orElse(errorCallback) // Ok(12)
// errorCallback was never called

const errVal = err('BOOOM!')
errVal.orElse((e) => err(true)) // Err(true)

Error Recovery with Fallback

enum DatabaseError {
  PoolExhausted = 'PoolExhausted',
  NotFound = 'NotFound',
}

const dbQueryResult: Result<string, DatabaseError> = 
  err(DatabaseError.NotFound)

const updatedResult = dbQueryResult.orElse((dbError) =>
  dbError === DatabaseError.NotFound
    ? ok('User does not exist')  // Recovery: convert error to success
    : err(500)                    // Convert to different error type
)
// updatedResult is Result<string, number>

Multi-Level Fallbacks

function fetchFromPrimaryDb(id: string): Result<User, 'PrimaryDbError'> {
  // ...
}

function fetchFromSecondaryDb(id: string): Result<User, 'SecondaryDbError'> {
  // ...
}

function fetchFromCache(id: string): Result<User, 'CacheError'> {
  // ...
}

const user = fetchFromPrimaryDb('123')
  .orElse(() => fetchFromSecondaryDb('123'))
  .orElse(() => fetchFromCache('123'))
  .orElse(() => ok(getDefaultUser()))
// Tries each source in order until one succeeds

Converting Errors to Success

type NetworkError = { code: number; message: string }

function fetchData(): Result<Data, NetworkError> {
  // ... network request
}

const result = fetchData().orElse((error) => {
  // Log the error for monitoring
  console.error('Network error:', error)
  
  // Return cached data as success
  return ok(getCachedData())
})
// result is Result<Data, never> - error is fully handled

Selective Error Recovery

type ApiError = 
  | { type: 'NetworkError'; retryable: boolean }
  | { type: 'ValidationError'; field: string }
  | { type: 'AuthError'; reason: string }

function callApi(): Result<Response, ApiError> {
  // ...
}

const result = callApi().orElse((error) => {
  // Only recover from network errors
  if (error.type === 'NetworkError' && error.retryable) {
    return callApi() // Retry
  }
  // Pass other errors through
  return err(error)
})

Chaining Recovery Strategies

enum PaymentError {
  InsufficientFunds = 'InsufficientFunds',
  CardDeclined = 'CardDeclined',
  NetworkError = 'NetworkError',
}

function chargeCard(amount: number): Result<Receipt, PaymentError> {
  // ...
}

function chargeSavedCard(amount: number): Result<Receipt, PaymentError> {
  // ...
}

function chargeWallet(amount: number): Result<Receipt, PaymentError> {
  // ...
}

const payment = chargeCard(100)
  .orElse((error) => {
    if (error === PaymentError.CardDeclined) {
      return chargeSavedCard(100)
    }
    return err(error)
  })
  .orElse((error) => {
    if (error === PaymentError.InsufficientFunds) {
      return chargeWallet(100)
    }
    return err(error)
  })

Real-World Example

function loadConfig(): Result<Config, 'FileNotFound'> {
  // Try to load from file
}

function getDefaultConfig(): Result<Config, never> {
  return ok({
    port: 3000,
    host: 'localhost',
    // ... default values
  })
}

const config = loadConfig()
  .orElse((error) => {
    console.warn('Config file not found, using defaults')
    return getDefaultConfig()
  })
// config is Result<Config, never> - always succeeds

Implementation Details

From the source code (result.ts:470-472):
orElse(f: any): any {
  return f(this.error)
}
For Ok (result.ts:366-368):
orElse(_f: any): any {
  return ok(this.value)
}

Notes

  • orElse operates on the error path, while andThen operates on the success path
  • Useful for implementing retry logic, fallbacks, and graceful degradation
  • The success type becomes a union of both possible success types
  • Can convert errors to successes for complete error recovery
  • For operations on Ok values, use andThen()

Build docs developers (and LLMs) love