ts-result provides a TypeScript implementation of the Result<T, E> type, commonly found in languages like Rust. It offers a mechanism for functions to return and propagate errors explicitly, without relying on exceptions or ambiguous return values like null or undefined.
A Result<T, E> is a discriminated union type representing one of two possibilities:
Ok<T, E>: Represents success and contains a value of typeT.Err<T, E>: Represents failure and contains an error value of typeE.
This library provides the Result<T, E> type alias, the underlying Ok and Err classes (though direct use is typically via factory functions), and a set of methods to interact with these types in a type-safe manner.
Instances are created using the factory functions:
Ok(value: T): Creates a success result containingvalue.Err(error: E): Creates an error result containingerror.
Traditional error handling in JavaScript/TypeScript often uses:
- Exceptions (
throw/try...catch): Implicitly alters control flow and requires callers to know when to catch. Errors are not part of the function signature. null/undefinedreturns: Ambiguous; doesn't distinguish between a valid "not found" state and an actual error. Type checking doesn't enforce handling.
Result addresses this by making potential errors an explicit part of the function's return type (() => Result<User, FetchError>). This forces callers to acknowledge and handle potential failures, leveraging the TypeScript compiler for verification.
- Explicit Error Handling: Functions explicitly declare their potential failure modes via the
Result<T, E>return type. - Type Safety: Utilizes TypeScript's discriminated unions and type guards (
isOk,isErr) for compile-time verification of error handling paths. - Method-Based Operations: Provides methods for transforming (
map,mapErr), chaining (andThen,orElse), and extracting values (unwrapOr,unwrapOrElse,match) without manual type checks in many cases. - Rust
ResultParity: Implements core methods found on Rust'sstd::result::Result(see API).
npm install @trylonai/ts-result
# or
yarn add @trylonai/ts-result
# or
pnpm add @trylonai/ts-resultimport { Ok, Err, Result } from '@trylonai/ts-result';
// Function returning a Result
function divide(
numerator: number,
denominator: number
): Result<number, string> {
if (denominator === 0) {
return Err('Division by zero'); // Return an Err variant
}
return Ok(numerator / denominator); // Return an Ok variant
}
const result1 = divide(10, 2); // Ok(5)
const result2 = divide(5, 0); // Err("Division by zero")
// 1. Checking the variant using type guards
if (result1.isOk()) {
// result1 is narrowed to Ok<number, string> here
console.log('Result 1 is Ok:', result1.value); // Output: Result 1 is Ok: 5
}
if (result2.isErr()) {
// result2 is narrowed to Err<number, string> here
console.error('Result 2 is Err:', result2.error); // Output: Result 2 is Err: Division by zero
}
// 2. Handling both cases with match
const message = result1.match({
Ok: (value) => `Success: ${value}`,
Err: (error) => `Failure: ${error}`,
});
console.log(message); // Output: Success: 5
// 3. Extracting the value safely
const value1 = result1.unwrapOr(0); // Extracts 5 from Ok(5)
const value2 = result2.unwrapOr(0); // Returns default 0 because result2 is Err
console.log(value1, value2); // Output: 5 0
// 4. Chaining operations
function checkPositive(n: number): Result<number, string> {
return n > 0 ? Ok(n) : Err('Number is not positive');
}
const positiveResult = divide(20, 4) // Ok(5)
.andThen(checkPositive); // Calls checkPositive(5) -> Ok(5)
const nonPositiveResult = divide(-10, 2) // Ok(-5)
.andThen(checkPositive); // Calls checkPositive(-5) -> Err("Number is not positive")
const errorResult = divide(10, 0) // Err("Division by zero")
.andThen(checkPositive); // Does not call checkPositive, remains Err("Division by zero")
console.log(positiveResult); // Ok { value: 5 }
console.log(nonPositiveResult); // Err { error: 'Number is not positive' }
console.log(errorResult); // Err { error: 'Division by zero' }
// 5. Unwrapping (Use cautiously - throws on Err)
try {
const unwrapped = result2.unwrap('Should have been Ok'); // Throws UnwrapError
} catch (e) {
if (e instanceof UnwrapError) {
console.error(e.message); // Output: Should have been Ok
console.error(e.causeValue); // Output: Division by zero
}
}Result<T, E> provides methods for:
- Checking:
isOk(),isErr()(act as type guards) - Extracting:
unwrap(),unwrapErr()(throw on incorrect variant),unwrapOr(),unwrapOrElse()(provide defaults) - Transforming:
map(),mapErr(),mapOr(),mapOrElse() - Chaining:
andThen(),orElse() - Boolean Logic:
and(),or() - Matching:
match()