Skip to main content

Overview

Similar to map, but the mapping function must return a new Result. This is useful for chaining computations where each step might fail, and is excellent for flattening nested Results.

Signature

class Result<T, E> {
  andThen<R extends Result<unknown, unknown>>(
    f: (t: T) => R
  ): Result<InferOkTypes<R>, InferErrTypes<R> | E>
  
  andThen<U, F>(f: (t: T) => Result<U, F>): Result<U, E | F>
}

Parameters

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

Returns

Returns a new Result<U, E | F> where:
  • If the original Result is Ok(value), returns the result of f(value)
  • If the original Result is Err(error), returns Err(error) without calling f
  • Error types from both Results are combined as a union type

Examples

Basic Chaining

import { ok, err } from 'neverthrow'

const sq = (n: number): Result<number, string> => ok(n ** 2)

ok(2)
  .andThen(sq)
  .andThen(sq) // Ok(16)

ok(2)
  .andThen(sq)
  .andThen(err) // Err(4)

ok(2)
  .andThen(err)
  .andThen(sq) // Err(2) - sq never called

Flattening Nested Results

// It's common to have nested Results
const nested = ok(ok(1234))
// nested is Result<Result<number, E2>, E1>

// Flatten using andThen with identity function
const notNested = nested.andThen((innerResult) => innerResult)
// notNested is Result<number, E1 | E2>

Sequential Operations

type User = { id: string; name: string }
type Post = { userId: string; title: string }

function validateUserId(id: string): Result<string, 'InvalidId'> {
  return id.length > 0 ? ok(id) : err('InvalidId')
}

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

function getUserPosts(user: User): Result<Post[], 'DatabaseError'> {
  // ... fetch posts
}

const result = validateUserId('123')
  .andThen(fetchUser)
  .andThen(getUserPosts)
// result is Result<Post[], 'InvalidId' | 'UserNotFound' | 'DatabaseError'>

Distinct Error Types (v4.1.0+)

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

function parseInput(raw: string): Result<Input, ParseError> {
  // ...
}

function validateInput(input: Input): Result<ValidInput, ValidationError> {
  // ...
}

function saveToDb(input: ValidInput): Result<void, DatabaseError> {
  // ...
}

const result = parseInput(raw)
  .andThen(validateInput)
  .andThen(saveToDb)
// result is Result<void, ParseError | ValidationError | DatabaseError>

Real-World Example

import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'

// Signatures:
// validateUser(user: User): Result<User, Error>
// insertUser(user: User): ResultAsync<User, Error>
// sendNotification(user: User): ResultAsync<void, Error>

const result = validateUser(user)
  .andThen(insertUser)
  .andThen(sendNotification)

result.then((res: Result<void, Error>) => {
  if (res.isErr()) {
    console.log('At least one step failed', res.error)
  } else {
    console.log('User validated, inserted and notified successfully')
  }
})

Error Short-Circuiting

const computation = ok(10)
  .andThen((n) => {
    console.log('Step 1:', n)
    return ok(n * 2)
  })
  .andThen((n) => {
    console.log('Step 2:', n)
    return err('Failed at step 2')
  })
  .andThen((n) => {
    console.log('Step 3:', n) // This won't be logged
    return ok(n + 5)
  })

// Output:
// Step 1: 10
// Step 2: 20
// computation is Err('Failed at step 2')

Implementation Details

From the source code (result.ts:337-339):
andThen(f: any): any {
  return f(this.value)
}
For Err (result.ts:461-463):
andThen(_f: any): any {
  return err(this.error)
}

Notes

  • andThen is also known as flatMap or bind in other functional programming libraries
  • Perfect for avoiding nested Results like Result<Result<T, E2>, E1>
  • All error types are preserved as a union type
  • For async operations, use asyncAndThen()
  • The operation short-circuits on the first error

Build docs developers (and LLMs) love