TypeScript Result type for functional error handling
npm install @moon7/result

A lightweight, zero-dependency TypeScript library for handling operations that might fail in a functional way.
- 🛡️ Type-safe error handling - Handle success and failure states without exceptions
- 🧩 Composable operations - Chain operations that might fail with clean, readable code
- 🔄 Async support - Full support for asynchronous operations with promises
- 🧪 Pattern matching - Elegant pattern matching for handling different result states
- 📦 Zero dependencies - Lightweight and focused utility
``bashnpm
npm install @moon7/result
Core Concepts
$3
The core of the library is the
Result type, which can be either a Success or a Failure:`typescript
type Result = Success | Failure;interface Success {
readonly value: V;
}
interface Failure {
readonly error: E;
}
`$3
The library includes a
Maybe type for handling optional values in a functional way. It's unified with the Result type, meaning all Result operations work seamlessly with Maybe:`typescript
import { some, none, isSome, isNone, Maybe } from '@moon7/result';
import { map, chain, unwrapOr } from '@moon7/result';// Creating Maybe values
const someValue = some(42); // Contains a value
const noneValue = none; // Represents absence of a value
// Type guards
if (isSome(someValue)) {
console.log(someValue.value); // 42
}
// Safely extracting values
const value = unwrapOr(someValue, 0); // 42
const fallback = unwrapOr(noneValue, 0); // 0
// Transformations
const doubled = map(someValue, x => x * 2); // some(84)
const chained = chain(someValue, x => x > 20 ? some(x) : none); // some(42)
`The Maybe type is implemented as a specialized Result where
Some is a Success and None is a Failure. This allows you to reuse all the Result utility functions with Maybe values.$3
The
Outcome type represents the common Node.js callback argument pattern of (error, value) tuples:`typescript
import { Outcome, fromOutcome } from '@moon7/result';// Outcome is a [error, value] tuple, common in Node.js callbacks
type SuccessOutcome = [undefined | null, V];
type FailureOutcome = [E, undefined];
type Outcome = SuccessOutcome | FailureOutcome;
// Converting from Outcome to Result
const nodeOutcome: Outcome = [null, "operation succeeded"];
// Success with "operation succeeded"
const result = fromOutcome(nodeOutcome);
const errorOutcome: Outcome = [new Error("operation failed"), undefined];
// Failure with Error("operation failed")
const errorResult = fromOutcome(errorOutcome);
`The Outcome type is primarily used:
- As a representation of Node.js callback argument tuples
- For conversion to Result via
fromOutcome() to leverage Result's rich APIBasic Usage
`typescript
import { success, failure, isSuccess, unwrapOr } from '@moon7/result';// Creating Results
const successResult = success(42);
const failureResult = failure(new Error("Something went wrong"));
// Checking result type
if (isSuccess(successResult)) {
console.log(successResult.value); // 42
}
// Safely extracting values with fallbacks
const value = unwrapOr(failureResult, 0); // 0
`$3
`typescript
import { fromTry, fromPromise } from '@moon7/result';// Safe synchronous operations
const result = fromTry(() => JSON.parse(someInput));
// Safe asynchronous operations
const asyncResult = await fromPromise(fetch('https://api.example.com/data'));
`$3
`typescript
import {
match, map, chain, recover, all, any,
unwrapOr, unwrapOrElse
} from '@moon7/result';// Pattern matching
const message = match(result, {
success: value =>
Got value: ${value},
failure: error => Error: ${error.message}
});// Transforming successful results
const doubled = map(result, x => x * 2);
// Chaining operations
const final = chain(result, value => {
return someOtherOperationThatMightFail(value);
});
// Recovering from errors
const recovered = recover(result, error => {
console.log(
Recovering from: ${error.message});
return defaultValue;
});// Working with multiple results
const combined = all([result1, result2, result3]); // Success only if ALL succeed
const any = any([result1, result2, result3]); // Success if ANY succeeds
`$3
The library provides full support for asynchronous operations:
`typescript
import { fromTryAsync, fromPromise } from '@moon7/result';// Creating async results
const result = await fromTryAsync(async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
});
// TypeScript narrows the type for safe value access
const value = isSuccess(result) ? result.value : defaultValue;
// Or use match pattern
const data = match(result, {
success: value => value,
failure: error => defaultValue
});
`$3
The library also provides an
AsyncResult type that adds a third "pending" state to represent loading operations:`typescript
import {
AsyncResult, pending, success, failure, matchAsync
} from '@moon7/result';// Component that displays user data with loading states
function UserProfile({ userId }: { userId: string }) {
// State to hold the AsyncResult
const [state, setState] = useState>(pending);
// Fetch user data when component mounts or userId changes
useEffect(() => {
async function fetchUser() {
// Start with pending state
setState(pending);
try {
// Simulate API call
const response = await fetch(
/api/users/${userId});
if (!response.ok) {
setState(failure(new Error("Error occurred")));
return;
}
const userData = await response.json();
setState(success(userData));
} catch (error) {
setState(failure(error));
}
}
fetchUser();
}, [userId]);
// Render different UI based on the AsyncResult state
return (
{matchAsync(state, {
pending: () => (
Loading user...
),
success: (user) => (
{user.name}
Email: {user.email}
Role: {user.role}
),
failure: (error) => (
Could not load user
{error.message}
)
})}
);
}
`$3
The library also provides a
fromNodeCallback and liftOutcome utilities that directly works with Node.js-style callback functions:`typescript
import { fromNodeCallback, liftOutcome } from '@moon7/result';
import { readFile } from 'fs';// Convert a Node.js callback function directly to a Promise
const fileResult = await fromNodeCallback(cb =>
readFile("package.json", "utf8", cb)
);
// Now you can use all Result operations
if (isSuccess(fileResult)) {
const content = fileResult.value;
// Process content...
}
// If you have a function with multiple Outcome callbacks
multipleCallbacks(
(err, val) => ...,
(err, val) => ...
);
// Use liftOutcome to work with result values instead
multipleCallbacks(
liftOutcome(result => ...),
liftOutcome(result => ...)
);
`This allows you to easily bridge Node.js callback-based APIs with the functional Result pattern.
$3
The library also provides utilities for assertions and nullable handling:
`typescript
import { must, strictMust, assert, assertNever } from '@moon7/result';// Check for null or undefined
const value = must(maybeNull, "Value cannot be null");
// Type assertions
assert(condition, "Condition must be true");
// Exhaustive type checking with assertNever
type Shape = Circle | Square | Triangle;
function processShape(shape: Shape) {
switch (shape.type) {
case 'circle':
return calculateCircleArea(shape);
case 'square':
return calculateSquareArea(shape);
case 'triangle':
return calculateTriangleArea(shape);
default:
// This ensures compiler error if you add a new shape type
// but forget to handle it in this switch statement
return assertNever(shape);
}
}
`The
assertNever function is particularly valuable for exhaustiveness checking in TypeScript. If you add a new variant to the Shape type but forget to handle it in the switch statement, TypeScript will give you a compile-time error, preventing potential bugs.$3
The library provides convenient ways to throw errors as expressions:
`typescript
import { raise } from '@moon7/result';// Throw an error as an expression in a ternary
const value = condition ? computeValue() : raise(new Error("Condition failed"));
// Use in place of default values
const item = items.find(i => i.id === id) ?? raise(new Error(
Item ${id} not found));
`API Reference
$3
- Result: Union type of Success and Failure
- Success: Represents a successful operation with a value
- Failure: Represents a failed operation with an error
- Maybe: Union type of Some and None for handling optional values
- Some: Represents a present value in a Maybe context
- None: Represents absence of a value in a Maybe context
- AsyncResult: Represents a value that can be pending, success, or failure
- Pending: Represents a pending/loading state
- Outcome: Tuple-based representation of [error, value] pairs$3
- isSuccess: Checks if a result is a Success
- isFailure: Checks if a result is a Failure
- isSome: Checks if a maybe is a Some
- isNone: Checks if a maybe is None
- isPending: Checks if an async result is Pending
- isResult: Checks if something is a Result
- isAsyncResult: Checks if something is an AsyncResult$3
- success: Creates a Success result
- failure: Creates a Failure result
- some: Creates a Some maybe value
- none: Constant representing None
- pending: Constant representing the pending state$3
- unwrap: Extracts the value or throws the error
- unwrapOr: Extracts the value or returns a default
- unwrapOr: Extracts the value or returns undefined
- unwrapOrElse: Extracts the value or computes a fallback$3
- recover: Transforms a failure into a success by recovering from the error$3
- fromTry: Creates a result from a function that might throw
- fromTryAsync: Creates a result from an async function that might throw
- fromPromise: Creates a result from a promise
- fromNullable: Creates a result from a nullable value
- fromNodeCallback: Creates a result from a Node.js style callback
- fromOutcome: Converts an Outcome to a Result
- fromMaybe: Converts a Maybe to a Result
- liftOutcome: Converts a Result callback to a Node-style callback$3
- all: Succeeds if all results succeed, fails on first failure
- any: Succeeds on first success, fails if all fail$3
- match: Applies success or failure function based on result
- matchAsync: Async version of match for AsyncResults$3
- map: Maps a success value, preserves failure
- chain: Maps a success to another result, preserves failure$3
- must: Ensures a value is not null or undefined
- strictMust: Ensures a value is not undefined
- assert: Throws if condition is false
- assertNever(value): Used for exhaustive checks in switch statements
- safely: Safely executes a function returning a default on error
- attempt: Similar to safely but returns a Result instead
- raiseContributions are welcome! Please feel free to submit a Pull Request.
The moon7 ecosystem includes several companion libraries that work well together:
- @moon7/async - Utilities for asynchronous programming
- @moon7/inspect - Advanced object inspection and formatting
- @moon7/validate - Validation library with composable rules
- @moon7/signals - Simple, lightweight signal/event system
MIT © Munir Hussin