A lightweight TypeScript Result type for safe error handling. Write predictable, composable code without throwing exceptions.
map, andThen, and morefromPromisenpm install @usersatoshi/results
Or with your preferred package manager:
bun add @usersatoshi/results
yarn add @usersatoshi/results
pnpm add @usersatoshi/results
import { ok, err, type Result } from '@usersatoshi/results';
const enum ErrorKind { NotFound = 0, Unauthorized = 1 }
type AppError = { kind: ErrorKind };
// Create an Ok result
const success: Result<number, AppError> = ok(42);
// Create an Err result
const failure: Result<number, AppError> = err({ kind: ErrorKind.NotFound });
// Use type guards (methods, not properties)
if (success.isOk()) {
console.log(success.value); // 42
}
if (failure.isErr()) {
console.log(failure.error); // { kind: 0 }
}
ok<T>(value: T): Ok<T>Creates an Ok result wrapping a successful value.
const result = ok(100);
err<E extends { kind: number }>(error: E): Err<E>Creates an Err result wrapping an error.
const result = err({ kind: 1 as const, message: "Something went wrong" });
fromPromise<T, E>(promise: Promise<T>, onErr: (error: unknown) => E): Promise<Result<T, E>>Converts a Promise into a Result, mapping rejections with your error handler.
const result = await fromPromise(
fetch('/api/data'),
(error) => ({ kind: 0 as const, message: String(error) })
);
Both Ok and Err implement the Result interface with methods for chaining operations:
isOk() - Type guard: narrows to Ok<T>isErr() - Type guard: narrows to Err<E>map(fn) - Transform the success value; propagates Err unchangedmapErr(fn) - Transform the error value; propagates Ok unchangedandThen(fn) - Chain Result-returning operations (flatMap)orElse(fn) - Recover from an error with a new Resultmatch(onOk, onErr) - Exhaustively handle both variantsunwrap() - Extract the value, throws ResultError if ErrunwrapOr(default) - Extract the value or return a defaultBaseResult.all(results) - Collect array of Results; returns first Err or Ok<T[]>BaseResult.any(results) - Return first Ok, or last Err if all failconst enum ErrorKind { DivisionByZero = 0 }
type MathError = { kind: ErrorKind };
function divide(a: number, b: number): Result<number, MathError> {
if (b === 0) {
return err({ kind: ErrorKind.DivisionByZero });
}
return ok(a / b);
}
const result = divide(10, 2);
if (result.isOk()) {
console.log(`Result: ${result.value}`); // Result: 5
}
ok(5)
.map(x => x * 2)
.andThen(x => x > 5 ? ok(x) : err({ kind: 0 as const }))
.match(
(value) => console.log(`Success: ${value}`),
(error) => console.log(`Error: ${error.kind}`)
);
const result = await fromPromise(
fetch('/api/users'),
(error) => ({ kind: 1 as const, message: String(error) })
);
if (result.isOk()) {
const users = await result.value.json();
console.log(users);
}
const results = [ok(1), ok(2), ok(3)];
const combined = BaseResult.all(results);
if (combined.isOk()) {
console.log(combined.value); // [1, 2, 3]
}
Works in all modern browsers that support ES2020+.
Apache-2.0