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:
Throwing (before)
Result (after)
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.
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:
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 )
}
Chain them with andThen
const result = validateAge ( 25 )
. andThen ( checkAdult )
result . match (
( age ) => `Valid adult age: ${ age } ` ,
( error ) => `Error: ${ error } `
)
// "Valid adult age: 25"
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.