The Result<T, E> type is the foundation of NeverThrow. It represents a computation that can either succeed with a value of type T (wrapped in Ok) or fail with an error of type E (wrapped in Err).
type Result<T, E> = Ok<T, E> | Err<T, E>
Source:result.ts:62This type is inspired by Rust’s Result type and provides a type-safe way to handle errors without throwing exceptions.
Unlike exceptions that can be thrown and forgotten, Result forces you to acknowledge the possibility of failure:
// With exceptions - easy to forget error handlingfunction riskyOperation(): number { throw new Error('Oops') // Caller may not know this throws}// With Result - error is in the signaturefunction safeOperation(): Result<number, Error> { return err(new Error('Oops')) // Caller MUST handle the error}
function getUserById(id: number): User { const user = database.findUser(id) // May throw if (!user) { throw new Error('User not found') // Hidden in implementation } return user}// Caller has no idea this might throwtry { const user = getUserById(123) console.log(user.name)} catch (error) { // We might forget this! console.error(error)}
function getUserById(id: number): Result<User, string> { const user = database.findUser(id) if (!user) { return err('User not found') // Explicit in return type } return ok(user)}// Caller knows this returns a Resultconst result = getUserById(123)// TypeScript forces us to handle both casesresult.match( (user) => console.log(user.name), (error) => console.error(error))
The Result type enables “railway-oriented programming” where your program flows on two tracks:
// Happy path (Ok track) ──────────────────────────>// ↓ error// Error path (Err track) ─────────────────────────>const result = parseInput(raw) // May fail at parsing .map(transform) // May fail at transform .andThen(validate) // May fail at validation .andThen(save) // May fail at saving// All errors are collected in the type:// Result<SavedData, ParseError | TransformError | ValidationError | SaveError>
Think of Result as a railway switch: operations on the “Ok track” continue the happy path, while operations on the “Err track” bypass remaining operations and carry the error forward.