@reiwuzen/result
v1.2.0
Published
A lightweight, zero-dependency TypeScript Result type for explicit, type-safe error handling — no exceptions needed.
Downloads
40
Maintainers
Readme
@reiwuzen/result
A lightweight, zero-dependency TypeScript Result type for explicit, type-safe error handling — no exceptions needed.
Installation
npm install @reiwuzen/resultWhy use Result?
Instead of throwing errors and hoping callers catch them, Result<T, E> forces you to handle both the success and failure cases at the type level.
// ❌ Throws — caller has no idea this can fail
function divide(a: number, b: number): number {
if (b === 0) throw new Error("Division by zero");
return a / b;
}
// ✅ Explicit — failure is part of the type signature
function divide(a: number, b: number): Result<number, string> {
if (b === 0) return Result.Err("Division by zero");
return Result.Ok(a / b);
}Basic Usage
import { Result } from "@reiwuzen/result";
const good = Result.Ok(42);
const bad = Result.Err("something went wrong");
console.log(good.ok); // true
console.log(good.value); // 42
console.log(bad.error); // "something went wrong"API
Creating Results
| Method | Description |
|--------|-------------|
| Result.Ok(value) | Creates a successful result |
| Result.Err(error) | Creates a failed result |
| Result.okOr(value, error) | Wraps a nullable — returns Err if null/undefined |
| Result.try(fn) | Wraps a throwing function, catching errors |
| Result.tryAsync(fn) | Async version of try |
| Result.all(results) | Collects Result[] into Result<T[]> — short-circuits on first Err |
Type Guards
const result = Result.Ok(42);
if (result.isOk()) {
console.log(result.value); // TypeScript knows this is safe
}
if (result.isErr()) {
console.log(result.error);
}Transforming Values
Result.Ok(5)
.map(n => n * 2) // Result<number, never> → Result<number, never>
.mapErr(e => `Error: ${e}`); // no-op on Ok
Result.Err("not found")
.mapErr(e => new Error(e)); // Result<never, string> → Result<never, Error>Chaining with andThen / flatMap
Use these to chain operations that themselves return a Result, avoiding Result<Result<T>>:
function parseNumber(s: string): Result<number, string> {
const n = Number(s);
return isNaN(n) ? Result.Err(`"${s}" is not a number`) : Result.Ok(n);
}
function divide(a: number, b: number): Result<number, string> {
return b === 0 ? Result.Err("Division by zero") : Result.Ok(a / b);
}
const result = parseNumber("10")
.andThen(n => divide(n, 2)); // Result<number, string>Pattern Matching with match
const message = result.match(
value => `Success: ${value}`,
error => `Failed: ${error}`
);Unwrapping
result.unwrapOr(0); // returns value or default
result.unwrapOrElse(e => -1); // returns value or calls fn with errorSide Effects (without consuming the Result)
result
.inspect(v => console.log("Got:", v)) // runs fn on Ok
.inspectErr(e => console.error("Error:", e)) // runs fn on Err
.map(v => v + 1);Handling Nullable Values
const user = getUser(id); // returns User | null
const result = Result.okOr(user, "User not found");
// Result<User, string>Wrapping Throwing Code
const result = Result.try(() => JSON.parse(rawInput));
// Result<unknown, unknown>
const asyncResult = await Result.tryAsync(() => fetch("/api/data").then(r => r.json()));
// Result<unknown, unknown>Collecting Multiple Results
const results = [Result.Ok(1), Result.Ok(2), Result.Ok(3)];
Result.all(results); // Result.Ok([1, 2, 3])
const withErr = [Result.Ok(1), Result.Err("oops"), Result.Ok(3)];
Result.all(withErr); // Result.Err("oops")License
MIT
