Skip to main content
This guide will teach you the fundamentals of NeverThrow through practical examples.

Basic Result types

Every Result is either Ok (success) or Err (failure). Create them using the ok() and err() functions:
import { ok, err } from 'neverthrow'

const success = ok({ myData: 'test' })
const failure = err('Oh noooo')

console.log(success.isOk())  // true
console.log(success.isErr()) // false

console.log(failure.isOk())  // false
console.log(failure.isErr()) // true
The Result type is defined as: type Result<T, E> = Ok<T, E> | Err<T, E> where T is the success value type and E is the error type.

Your first Result-returning function

Instead of throwing errors, return a Result that makes success and failure explicit:
function parseJSON(jsonString: string): unknown {
  return JSON.parse(jsonString) // throws on invalid JSON
}

// Caller doesn't know this can throw
const data = parseJSON(userInput)
Key insight: With Result, errors become part of your function’s type signature, making them impossible to ignore.

Transforming Results with map

Use .map() to transform the success value while leaving errors untouched:
import { ok, err } from 'neverthrow'

const result = ok([1, 2, 3, 4])
  .map(arr => arr.map(n => n * 2))
  .map(arr => arr.reduce((a, b) => a + b))

result.isOk() // true
result._unsafeUnwrap() // 20

// Errors pass through unchanged
const errorResult = err('failed')
  .map(arr => arr.map(n => n * 2))
  
errorResult.isErr() // true

Chaining with andThen

Use .andThen() when your next operation might also fail:
1

Define functions that return Results

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

function validateAge(age: number): Result<number, string> {
  if (age < 0) return err('Age cannot be negative')
  if (age > 150) return err('Age seems unrealistic')
  return ok(age)
}

function checkAdult(age: number): Result<number, string> {
  if (age < 18) return err('Must be 18 or older')
  return ok(age)
}
2

Chain them with andThen

const result = validateAge(25)
  .andThen(checkAdult)

result.match(
  (age) => `Valid adult age: ${age}`,
  (error) => `Error: ${error}`
)
// "Valid adult age: 25"
3

Short-circuits on first error

const result = validateAge(-5)
  .andThen(checkAdult)

result.match(
  (age) => `Valid adult age: ${age}`,
  (error) => `Error: ${error}`
)
// "Error: Age cannot be negative"
map vs andThen: Use map when your transformation cannot fail. Use andThen when your transformation returns another Result.

Handling both cases with match

Use .match() to handle both success and error cases and return a final value:
import { ok, err } from 'neverthrow'

function divide(a: number, b: number): Result<number, string> {
  if (b === 0) return err('Division by zero')
  return ok(a / b)
}

const message = divide(10, 2).match(
  (value) => `Result: ${value}`,
  (error) => `Error: ${error}`
)

console.log(message) // "Result: 5"
Alternatively, use .unwrapOr() to provide a default value for errors:
const value = divide(10, 0).unwrapOr(0)
console.log(value) // 0

Working with async code

For asynchronous operations, use ResultAsync which behaves like a Promise but with Result methods:
import { ResultAsync, okAsync, errAsync } from 'neverthrow'

function fetchUser(id: string): ResultAsync<User, string> {
  return ResultAsync.fromPromise(
    fetch(`/api/users/${id}`).then(r => r.json()),
    () => 'Failed to fetch user'
  )
}

function validateUser(user: User): Result<User, string> {
  if (!user.email) return err('Email is required')
  return ok(user)
}

// Chain sync and async operations seamlessly
const result = await fetchUser('123')
  .andThen(validateUser)
  .map(user => user.email)

result.match(
  (email) => console.log('Email:', email),
  (error) => console.error('Error:', error)
)
ResultAsync is thenable, so you can use it with await or .then() just like a regular Promise.

Combining multiple Results

Use Result.combine() to aggregate multiple Results into a single Result:
import { ok, err, Result } from 'neverthrow'

const results = [
  ok(1),
  ok(2),
  ok(3)
]

const combined = Result.combine(results)
// Ok([1, 2, 3])

// Short-circuits on first error
const resultsWithError = [
  ok(1),
  err('failed'),
  ok(3)
]

const combinedWithError = Result.combine(resultsWithError)
// Err('failed')
To collect all errors instead of short-circuiting, use Result.combineWithAllErrors():
const results = [
  ok(123),
  err('error 1'),
  ok(456),
  err('error 2')
]

const combined = Result.combineWithAllErrors(results)
// Err(['error 1', 'error 2'])

Next steps

You now know the basics of NeverThrow. Here’s what to explore next:

Core concepts

Learn about the Result type in depth

Async operations

Master ResultAsync for promises

Error recovery

Handle and recover from errors with orElse

API reference

Browse all available methods
Remember: Never use ._unsafeUnwrap() in production code. It should only be used in test environments. Use .match() or .unwrapOr() instead.

Build docs developers (and LLMs) love