Creating Results
NeverThrow provides two core types for encoding success and failure:
Creating Ok Values
Use ok() to create a successful result:
import { ok } from 'neverthrow'
const result = ok ( 42 )
result . isOk () // true
result . isErr () // false
You can wrap any value, including null and undefined:
const nullResult = ok ( null )
const voidResult = ok ( undefined )
// Both are valid Ok values
nullResult . isOk () // true
voidResult . isOk () // true
Creating Err Values
Use err() to create an error result:
import { err } from 'neverthrow'
const result = err ( 'Something went wrong' )
result . isOk () // false
result . isErr () // true
Checking Result State
Type Guards with isOk and isErr
Use isOk() and isErr() to check and narrow the type:
import { Result , 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 result = divide ( 10 , 2 )
if ( result . isOk ()) {
// TypeScript knows this is Ok<number, string>
console . log ( result . value ) // 5
} else {
// TypeScript knows this is Err<number, string>
console . log ( result . error ) // Won't execute in this case
}
Pattern Matching with match
Handle both cases at once using match():
const result = divide ( 10 , 2 )
const message = result . match (
( value ) => `Success: ${ value } ` ,
( error ) => `Error: ${ error } `
)
console . log ( message ) // "Success: 5"
Both callbacks must return the same type, and match unwraps the Result:
const result : Result < number , string > = ok ( 42 )
// Both functions return strings
const output : string = result . match (
( num ) => num . toString (),
( err ) => `Error: ${ err } `
)
map() transforms the value inside an Ok, leaving Err untouched:
const result = ok ( 12 )
. map ( n => n * 2 )
. map ( n => n . toString ())
result . _unsafeUnwrap () // "24"
If the Result is an Err, map is skipped:
const result = err ( 'failed' )
. map ( n => n * 2 ) // Not called
. map ( n => n . toString ()) // Not called
result . _unsafeUnwrapErr () // "failed"
mapErr() transforms the error, leaving Ok values untouched:
const result = err ( '404' )
. mapErr ( code => `Error code: ${ code } ` )
result . _unsafeUnwrapErr () // "Error code: 404"
Useful for adding context to errors:
function parseJSON ( input : string ) : Result < unknown , string > {
try {
return ok ( JSON . parse ( input ))
} catch ( e ) {
return err ( 'Parse failed' )
}
}
const result = parseJSON ( 'invalid' )
. mapErr ( err => ({ message: err , timestamp: Date . now () }))
// Error now includes timestamp
Using unwrapOr for Default Values
unwrapOr() extracts the value or returns a default:
const okResult = ok ( 42 )
const errResult = err ( 'oops' )
okResult . unwrapOr ( 0 ) // 42
errResult . unwrapOr ( 0 ) // 0
Real-World Example
With Error Recovery
function getUsername ( userId : string ) : Result < string , string > {
// ... database lookup
return ok ( 'alice' )
}
// Display username or fallback
const displayName = getUsername ( '123' )
. map ( name => name . toUpperCase ())
. unwrapOr ( 'Anonymous' )
console . log ( `Welcome, ${ displayName } !` )
const config = loadConfig ()
. map ( cfg => cfg . timeout )
. unwrapOr ( 5000 ) // Default timeout
console . log ( `Using timeout: ${ config } ms` )
Safe Unwrapping in Tests
For testing, use _unsafeUnwrap() and _unsafeUnwrapErr():
import { expect } from 'vitest'
const result = ok ( 42 )
expect ( result . _unsafeUnwrap ()). toBe ( 42 )
const error = err ( 'failed' )
expect ( error . _unsafeUnwrapErr ()). toBe ( 'failed' )
Never use _unsafeUnwrap() in production code. These methods throw exceptions if called on the wrong variant and should only be used in test environments.
Comparing Results
Result instances are comparable for testing:
import { expect } from 'vitest'
expect ( ok ( 42 )). toEqual ( ok ( 42 ))
expect ( ok ( 42 )). not . toEqual ( ok ( 43 ))
expect ( err ( 42 )). toEqual ( err ( 42 ))
expect ( err ( 42 )). not . toEqual ( err ( 43 ))
Here’s a real-world example combining basic operations:
import { Result , ok , err } from 'neverthrow'
interface User {
email : string
age : number
}
function validateEmail ( email : string ) : Result < string , string > {
if ( email . includes ( '@' )) {
return ok ( email )
}
return err ( 'Invalid email format' )
}
function validateAge ( age : number ) : Result < number , string > {
if ( age >= 18 ) {
return ok ( age )
}
return err ( 'Must be 18 or older' )
}
// Validate and transform
const emailResult = validateEmail ( '[email protected] ' )
const ageResult = validateAge ( 25 )
if ( emailResult . isOk () && ageResult . isOk ()) {
const user : User = {
email: emailResult . value ,
age: ageResult . value
}
console . log ( 'User created:' , user )
}
// Or use match for immediate feedback
const emailStatus = validateEmail ( 'invalid-email' ). match (
( email ) => `Valid: ${ email } ` ,
( error ) => `Error: ${ error } `
)
console . log ( emailStatus ) // "Error: Invalid email format"
Next Steps
Chaining Operations Learn how to compose multiple operations with andThen
Async Operations Work with asynchronous code using ResultAsync