@rsnk/result
v1.0.0
Published
A TypeScript port of Rust's Result<T, E> type, providing a type-safe way to handle errors and avoid exceptions.
Downloads
124
Maintainers
Readme
Result
A TypeScript port of Rust's Result<T, E> type, providing a type-safe way to handle errors and avoid exceptions.
Table of Contents
- Installation
- Overview
- Quick Start
- Common Patterns
- TypeScript Type Narrowing
- Comparison with Other Approaches
- Design Philosophy
- Contributing
- License
- API Reference
Installation
Install the package using your favorite package manager:
npm install @rsnk/resultor
yarn add @rsnk/resultOverview
The Result type represents either success (Ok) or failure (Err). This pattern eliminates the ambiguity of throwing exceptions and makes error handling explicit and type-safe.
// Instead of this:
try {
const user = getUser(id);
console.log(user.name);
} catch (e) {
console.error("Failed:", e);
}
// Write this:
const result = getUser(id); // Result<User, Error>
result.match({
Ok: user => console.log(user.name),
Err: err => console.error("Failed:", err)
});Quick Start
import R from "@rsnk/result";Creating Results
// Create a success (Ok)
const success = R.ok(42); // Result<number, never>
// Create a failure (Err)
const failure = R.err("not found"); // Result<never, string>
// Wrap a function that might throw
const res = R.tryCatch(() => JSON.parse(str)); // Result<any, unknown>
// Wrap a Promise
const asyncRes = await R.fromPromise(fetch("/api/data")); // Result<Response, unknown>Basic Operations
// Check status
if (result.isOk()) {
console.log(result.unwrap());
}
// Transform values
result
.map(x => x * 2)
.mapErr(err => `Error: ${err}`)
.unwrapOr(0);
// Pattern matching
result.match({
Ok: value => console.log("Success:", value),
Err: error => console.error("Error:", error)
});Common Patterns
Chaining Operations (Railway Oriented Programming)
Propagate errors automatically while applying transformations to successful values.
function getProcessedData(input: string): Result<number, string> {
return parseInput(input) // Result<Input, string>
.andThen(validateInput) // Result<Input, string>
.map(processData) // Result<Data, string>
.map(data => data.value); // Result<number, string>
}Async Error Handling
Handle async operations without try-catch hell.
const result = await R.fromPromise(fetchUser(id))
.andThenAsync(async user => await fetchPreferences(user.id))
.map(prefs => prefs.theme);Safe Unwrapping
Avoid throwing exceptions when accessing values.
// Safe defaults
const port = getConfig("port")
.map(Number)
.unwrapOr(8080);
// Computed defaults
const config = loadConfig()
.unwrapOrElse(err => defaultConfig);TypeScript Type Narrowing
The Result type integrates seamlessly with TypeScript's type system:
const res: Result<number, string> = someOperation();
// Type guards work automatically
if (res.isOk()) {
const val: number = res.unwrap(); // Safe!
} else {
const err: string = res.unwrapErr(); // Safe!
}
// Type predicates
if (res.isOkAnd(x => x > 10)) {
// TypeScript knows it's Ok and potentially narrows type based on predicate
}Comparison with Other Approaches
vs Exceptions
Exceptions:
- Implicit control flow (GOTO).
- Easy to forget to handle.
- Function signatures don't reveal possible errors.
Result:
- Explicit control flow.
- Forced error handling (cannot access value without handling error).
- Function signatures document success and failure types (
Result<User, DatabaseError>).
vs Returning null/false
Returning null/false:
- No error context (why did it fail?).
- Ambiguous (is
nulla valid value or an error?).
Result:
Errcontains rich error information.- Clear distinction between success value and error value.
Design Philosophy
This implementation prioritizes developer experience and productivity. Key design decisions:
- Class-Based: For better IDE autocomplete and method chaining.
- Separate Ok/Err Classes: Optimized for performance and clean implementation.
- Heavy Async Support: First-class support for
Promiseintegration (mapAsync,andThenAsync), as modern TS is heavily async.
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
MIT
API Reference
Factory Functions
R.ok<T, E>(value: T): Result<T, E>
Creates a success Ok result.
R.err<E, T>(error: E): Result<T, E>
Creates a failure Err result.
R.tryCatch<T, E>(fn: () => T): Result<T, E>
Executes a function and returns Ok(value) or Err(error) if it throws.
R.fromPromise<T, E>(promise: Promise<T>): Promise<Result<T, E>>
Converts a Promise to a Result. Resolves to Ok or rejects to Err.
Type Checking
isOk(): this is Ok<T, E>
Returns true if the result is Ok.
isErr(): this is Err<T, E>
Returns true if the result is Err.
isOkAnd(predicate: (value: T) => boolean): boolean
Returns true if Ok and the predicate matches.
isErrAnd(predicate: (error: E) => boolean): boolean
Returns true if Err and the predicate matches.
Extraction (Unwrapping)
unwrap(): T
Returns the value. Throws if Err.
unwrapErr(): E
Returns the error. Throws if Ok.
expect(msg: string): T
Returns the value. Throws with message if Err.
unwrapOr<U>(default: U): T | U
Returns the value or a default.
unwrapOrElse<U>(fn: (err: E) => U): T | U
Returns the value or computes a default from the error.
Transformations
map<U>(fn: (val: T) => U): Result<U, E>
Transforms the Ok value.
mapErr<F>(fn: (err: E) => F): Result<T, F>
Transforms the Err value.
mapOr<U>(default: U, fn: (val: T) => U): U
Maps and unwraps, returning a default if Err.
mapOrElse<U>(defaultFn: (err: E) => U, fn: (val: T) => U): U
Maps and unwraps, computing default from error if Err.
flatten()
Flattens Result<Result<T, E>, E> to Result<T, E>.
Chaining
andThen<U>(fn: (val: T) => Result<U, E>): Result<U, E>
Chains operations that may fail (flatMap).
and<U>(res: Result<U, E>): Result<U, E>
Returns res if Ok, otherwise returns self (Err).
or<F>(res: Result<T, F>): Result<T, F>
Returns self if Ok, otherwise returns res.
orElse<F>(fn: (err: E) => Result<T, F>): Result<T, F>
Returns self if Ok, otherwise calls fn to recover from error.
Async
mapAsync<U>(fn: (val: T) => Promise<U>): Promise<Result<U, E>>
Async version of map.
andThenAsync<U>(fn: (val: T) => Promise<Result<U, E>>): Promise<Result<U, E>>
Async version of andThen.
transposePromise()
Converts Result<Promise<T>, E> to Promise<Result<T, E>>.
Side Effects
inspect(fn: (val: T) => void): Result<T, E>
Runs fn if Ok.
inspectErr(fn: (err: E) => void): Result<T, E>
Runs fn if Err.
Pattern Matching
match({ Ok: fn, Err: fn }): U
Pattern matching.
