Stop throwing errors, and instead return Results!
neverthrow is a TypeScript library that replaces exception-based error handling with a functional Result type. Instead of throwing errors and relying on try-catch blocks, functions return a Result<T, E> that explicitly represents either success (Ok
The package provides both synchronous (Result) and asynchronous (ResultAsync) types, enabling consistent error handling patterns across your codebase. With over 1.2 million weekly downloads and 408 dependent packages, it has become the most popular Rust-style Result library in the TypeScript ecosystem. The library is particularly valuable in applications where type safety and explicit error flows are critical—such as APIs, data pipelines, and financial systems.
neverthrow's chaining methods like andThen() and map() allow you to build error-propagating pipelines where failures automatically short-circuit without manual error checks. This eliminates deeply nested if-statements and makes error paths linear and readable. The library integrates with ESLint through @okee-tech/eslint-plugin-neverthrow to enforce that Results are always consumed, preventing silent failures where errors are ignored.
Developers typically adopt neverthrow when scaling TypeScript applications that suffer from inconsistent error handling, when building RPC systems where typed errors improve frontend integration, or when migrating from less-maintained alternatives like ts-results. The explicit error types enable better logging, internationalization through error codes, and compile-time guarantees about error cases.
import { ok, err, Result, ResultAsync } from 'neverthrow';
interface User { id: string; email: string; }
type DbError = 'NOT_FOUND' | 'CONNECTION_ERROR';
type ValidationError = 'INVALID_EMAIL' | 'EMAIL_IN_USE';
function findUser(id: string): ResultAsync<User, DbError> {
return ResultAsync.fromPromise(
fetch(`/api/users/${id}`).then(r => r.json()),
() => 'CONNECTION_ERROR' as const
).andThen(data =>
data.user ? ok(data.user) : err('NOT_FOUND')
);
}
function validateEmail(email: string): Result<string, ValidationError> {
return email.includes('@')
? ok(email)
: err('INVALID_EMAIL');
}
function updateUserEmail(userId: string, newEmail: string): ResultAsync<User, DbError | ValidationError> {
return ResultAsync.fromSafePromise(validateEmail(newEmail))
.andThen(validEmail =>
findUser(userId).andThen(user => {
user.email = validEmail;
return ResultAsync.fromPromise(
fetch(`/api/users/${userId}`, {
method: 'PATCH',
body: JSON.stringify({ email: validEmail })
}).then(r => r.json()),
() => 'CONNECTION_ERROR' as const
);
})
);
}
updateUserEmail('123', 'new@example.com')
.match(
user => console.log('Updated:', user),
error => {
if (error === 'INVALID_EMAIL') console.error('Email format invalid');
if (error === 'NOT_FOUND') console.error('User not found');
if (error === 'CONNECTION_ERROR') console.error('Database unreachable');
}
);API request handling: Wrap fetch calls in ResultAsync to chain validation, parsing, and transformation steps. Each step propagates errors automatically without nested try-catch blocks, and the final Result explicitly shows both success and all possible error types.
Form validation pipelines: Chain validation rules where each validator returns a Result. Use andThen() to pass validated data to the next rule, automatically stopping on the first failure and returning a typed error with field-specific messages.
Database operations: Wrap queries in Results to handle connection failures, constraint violations, and not-found cases as explicit error types. Compose multiple queries with andThen() so transactions roll back cleanly when any step fails.
Configuration parsing: Parse environment variables or config files into typed configuration objects, returning specific error types for missing keys, invalid formats, or schema violations—making startup failures debuggable.
RPC/tRPC integration: Return Results from API endpoints so frontend code receives fully typed success and error cases. This eliminates runtime error parsing and enables exhaustive pattern matching on error types for precise UI feedback.
npm install neverthrowpnpm add neverthrowbun add neverthrow