A highly optimized, fully type-safe, dependency-free utility for representing operations that can either succeed or fail.
Complete with:
- Comprehensive functional transformations
- Advanced error handling capabilities
- Async/Promise integration
- Performance optimizations
- Elegant composition patterns
Its a fuzzy approach where by having better results we can craft more reliable code that doesn't rely on try/catch
and exceptions for control flow.
This library aims to provide an elegant solution for handling operations that can succeed or fail in TypeScript applications. It addresses the common problems with traditional exception-based error handling while maintaining full type safety and composability.
Unlike traditional error handling with exceptions, this library seeks to enable us with the following:
- Explicit error handling with proper type information
- Composable operations that might fail
- Functional transformations of success and error values
- Predictable control flow without hidden exceptional paths
- Better testability through deterministic error handling
- Performance optimizations for high-throughput scenarios
-
🧙♂️ Type-Safe Results - Experience bulletproof type safety with full TypeScript inference and precise error typing. No more "any" type nightmares or runtime surprises.
-
🔄 Powerful Functional Transformations - Transform your data with elegant, chainable operations like
map
,chain
, andpipe
. Build complex processing flows with minimal code. -
👪 Painless Composition - Compose operations that might fail without deeply nested try/catch blocks. Create clean, readable code that's easy to maintain and extend.
-
🧬 First-Class Async Support - Handle async operations with the same elegant API. Convert Promises to Results, chain async operations, and process multiple async results in parallel.
-
⚠️ Explicit Error Handling - Say goodbye to forgotten try/catch blocks and silent failures. Every potential error becomes a value you can inspect, transform, and handle with precision. -
😇 Control Your Own Destiny - Decide exactly when and how to extract values from results with smart unwrapping operations that prevent runtime crashes.
-
🚂 Railway-Oriented Programming - Implement the powerful railway pattern with built-in short-circuiting. Keep your happy path clean while ensuring errors naturally flow to error handlers.
-
⚡ Blazing Fast Performance - Meticulously optimized for speed and minimal memory usage. We've benchmarked extensively to ensure near-zero overhead compared to traditional error handling.
-
🤑 Rich Combinators - Combine multiple results with sophisticated utilities like
asyncAll
and sophisticated error aggregation. No more manual result merging. -
💻 Developer Experience First - Designed for humans with clear naming, consistent behavior, and detailed documentation with practical examples for every function.
-
🆓 Zero Dependencies - Not a single external dependency. Just pure, optimized TypeScript that won't bloat your bundle or introduce security vulnerabilities.
-
💚 Universal Compatibility - Works anywhere JavaScript runs: browsers, Node.js, Deno, Bun, web workers, or serverless functions. One API to rule them all.
-
🪖 Battle-Tested - Comprehensive guides with real-world examples for every function. Learn through practical code, not just abstract theory.
# Using npm
npm install @fuzzy-street/results
# Using yarn
yarn add @fuzzy-street/results
# Using pnpm
pnpm add @fuzzy-street/results
Result Pattern is built around a simple concept: a function that can either succeed or fail returns a Result<T, E>
type, which is either:
{ status: "success", data: T }
for successful operations{ status: "error", error: E }
for failed operations
This allows you to:
- Make error handling explicit and type-safe
- Create pipelines of operations where errors naturally short-circuit
- Eliminate try/catch blocks for control flow
- Ensure all error cases are handled
import { success, error, isSuccess, isError, match } from '@fuzzy-street/results';
// Creating success results
const successResult = success(42);
// { status: "success", data: 42 }
// Creating error results
const errorResult = error(new Error('Something went wrong'));
// { status: "error", error: Error("Something went wrong") }
// Type checking
if (isSuccess(successResult)) {
console.log(successResult.data); // 42
}
if (isError(errorResult)) {
console.log(errorResult.error.message); // "Something went wrong"
}
// Pattern matching
const message = match(successResult, {
success: (value) => `Success: ${value}`,
error: (err) => `Error: ${err.message}`
});
// "Success: 42"
The library is organized into three main categories:
These form the foundation of the Result pattern:
success
: Creates a successful resulterror
: Creates an error resultisSuccess
: Type guard for successful resultsisError
: Type guard for error resultsisResult
: Type guard for any resultmatch
: Pattern matching for resultsunwrap
: Extracts value or throws errorunwrapOr
: Extracts value with fallback
These help transform and process results:
map
: Transforms success valuesmapError
: Transforms error valueschain
: Chains operations that might failtap
: Performs side effects on any resulttapSuccess
: Performs side effects on success resultstapError
: Performs side effects on error resultspipe
: Creates transformation pipelinescreateErrorBoundary
: Creates error-handling boundaries
These handle asynchronous operations:
fromPromise
: Converts a Promise to a ResultfromAsync
: Wraps an async function to return a ResultasyncMap
: Maps a Result value asynchronouslyasyncMapError
: Maps a Result error asynchronouslyasyncChain
: Chains async operations that return ResultsasyncPipe
: Creates async transformation pipelinesasyncAll
: Combines multiple async ResultswithFinally
: Executes cleanup after async operationscreateAsyncErrorBoundary
: Creates async error-handling boundaries
Each function has detailed documentation and examples in the linked markdown files. We highly recommend exploring these docs to understand the full capabilities of each utility.
import { pipe, success, error } from '@fuzzy-street/results';
function validateInput(input: string) {
return input ? success(input) : error(new Error('Input required'));
}
function processInput(input: string) {
return input.length >= 3
? success(input.toUpperCase())
: error(new Error('Input too short'));
}
function formatOutput(input: string) {
return success(`Processed: ${input}`);
}
// Create a processing pipeline
const result = pipe(
'hello', // Input value
validateInput, // Validate (short-circuit if invalid)
processInput, // Process (short-circuit if fails)
formatOutput // Format output
);
// result: { status: "success", data: "Processed: HELLO" }
import { asyncChain, fromPromise } from '@fuzzy-street/results';
async function fetchUser(id: string) {
return fromPromise(
fetch(`https://api.example.com/users/${id}`)
.then(response => {
if (!response.ok) throw new Error(`HTTP error ${response.status}`);
return response.json();
})
);
}
async function fetchUserPosts(user) {
return fromPromise(
fetch(`https://api.example.com/users/${user.id}/posts`)
.then(response => {
if (!response.ok) throw new Error(`HTTP error ${response.status}`);
return response.json();
})
);
}
// Chain async operations
async function getUserWithPosts(userId: string) {
const userResult = await fetchUser(userId);
return await asyncChain(userResult, fetchUserPosts);
}
import { match, success, error } from '@fuzzy-street/results';
function divideNumbers(a: number, b: number) {
return b !== 0
? success(a / b)
: error(new Error('Division by zero'));
}
const result = divideNumbers(10, 0);
// Pattern matching for elegant error handling
const message = match(result, {
success: (value) => `Result: ${value}`,
error: (err) => `Error: ${err.message}`
});
// message: "Error: Division by zero"
The Result pattern has been carefully optimized to minimize overhead:
- Minimal object creation in transformations
- Early short-circuiting to avoid unnecessary processing
- Efficient memory usage with optimized data structures
- Benchmarks show minimal overhead compared to direct function calls
According to our benchmarks:
- Function call overhead is acceptable for most real-world use cases
- Error handling with Results is actually faster than traditional try/catch in many scenarios
- Memory overhead is minimal
This library is built with TypeScript first in mind:
- Full type inference for all operations
- Generic type parameters for flexible usage
- Type guards for runtime type checking
- Type narrowing for improved developer experience
This repository comes with a comprehensive set of examples to demonstrate the usage of each function. To run them locally:
-
Clone the repository:
git clone https://github.com/fuzzy-st/results.git cd results
-
Install dependencies:
pnpm install
-
Run a specific example:
pnpm tsx src/examples/core/success.ts pnpm tsx src/examples/transformers/pipe.ts pnpm tsx src/examples/async/asyncChain.ts
Each example file contains multiple usage patterns. To try a specific example, uncomment the corresponding run...()
function at the bottom of the file before running it.
For example, in src/examples/core/success.ts
:
// Uncomment any of these to run the example
// runBasicExample();
// runValidationExample();
runComplexExample(); // This one will execute
We encourage you to explore these examples to get a feel for how the library works in practice. Each example corresponds to the documentation for its respective function.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.