Introduction
Type-safe error handling for TypeScript with Result, Maybe, Try, AsyncResult, and Error monads
@deessejs/fp is a TypeScript library that brings functional programming patterns to error handling. It provides zero-runtime-dependency monads with a clean, composable API.
This library is inspired by fp-ts, Rust's std::result, and other functional programming traditions—adapted for idiomatic TypeScript.
The Problem with Traditional Error Handling
Traditional error handling in TypeScript relies heavily on exceptions and try-catch blocks. But try/catch works. Until it doesn't.
1. Errors Lose Context
// try/catch: Error loses all context
try {
const user = await fetchUser(id);
const profile = await fetchProfile(user.profileId);
const posts = await fetchPosts(profile.id);
} catch (e) {
logger.error(e); // What failed? Where? Why?
}When this fails, you have no idea:
- Was it
fetchUser?fetchProfile?fetchPosts? - What was the ID that failed?
- Was it a network error? A 404? A timeout?
2. Types Lie to You
TypeScript promises type safety everywhere except exceptions:
function fetchUser(id: string): User {
// TypeScript says: returns User
// Reality: might throw, might return null
}With try/catch, the type system can't tell you what can fail.
3. Nested try/catch is Ugly
try {
try {
const user = await fetchUser(id);
try {
const profile = await fetchProfile(user.profileId);
} catch (e) {
// Handle profile error
}
} catch (e) {
// Handle user error
}
} catch (e) {
// Handle... what exactly?
}The Result Alternative
@deessejs/fp makes errors explicit in your types:
import { fromPromise, ok, err } from '@deessejs/fp';
const result = await fromPromise(fetchUser(id))
.flatMap(user => fromPromise(fetchProfile(user.profileId)))
.flatMap(profile => fromPromise(fetchPosts(profile.id)))
.mapErr(e => e.addNotes('Failed to load user feed'));
// TypeScript KNOWS this might fail
// Error has FULL context| try/catch | @deessejs/fp |
|---|---|
| Errors lose context | Errors have rich context |
| Types lie | Types tell the truth |
| Nested mess | Flat, composable chains |
| Silent failures possible | Compiler enforces handling |
| Unknown failure modes | Explicit error types |
Did you notice the function signature says it returns number, but it can actually throw? TypeScript doesn't tell you about the potential for failure.
Enter Result: Explicit Error Handling
The Result type makes errors explicit in your type signatures. Instead of hoping a function won't throw, the return type tells you exactly what can go wrong:
import { ok, err, map, getOrElse } from "@deessejs/fp";
const divide = (a: number, b: number): Result<number, string> => {
if (b === 0) return err("Division by zero");
return ok(a / b);
};
// The type system forces you to handle both cases
const result = divide(10, 2)
.map(x => x * 2) // Transform the value
.getOrElse(0); // Provide default if error
console.log(result); // 5Now the type signature Result<number, string> tells you: "this returns a number, but it might fail with a string error."
Why @deessejs/fp?
| Feature | Benefit |
|---|---|
| Zero runtime dependencies | Lightweight bundle, no bloat |
| Full TypeScript support | Complete type inference |
| Composable API | Chain operations with map, flatMap |
| Functional patterns | map, flatMap, tap, match, and more |
| Multiple types | Result, Maybe, Try, AsyncResult |
| Error System | Python-inspired error handling with notes, cause chains, and groups |
Core Types
Our library provides multiple types for different scenarios:
Maybe
Handle optional values without null/undefined
Result
Explicit success/failure with typed errors
Try
Wrap synchronous functions that might throw
AsyncResult
Async operations with proper error typing
Getting Started
Install the library:
pnpm add @deessejs/fpThen import and start using it:
import { ok, err, map, getOrElse, some, none } from "@deessejs/fp";
// Create a Result
const success = ok(42);
const failure = err("Something went wrong");
// Transform without losing error context
const result = ok(10)
.map(x => x * 2)
.map(x => x + 1);
// Extract the value
const value = result.getOrElse(0);
// Work with optional values
const name = some("Alice");
const noName = none();
const greeting = name
.map(n => `Hello, ${n}!`)
.getOrElse("Hello, stranger!");Next Steps
Ready to dive in? Here's where to go next:
Installation
Set up @deessejs/fp in your project
Maybe
Start with optional values
Result
The foundation of explicit error handling
Examples
Real-world usage patterns
Who Maintains This?
@deessejs/fp is actively maintained by Nesalia Inc., the organization behind the DeesseJS framework and enterprise tools.
Our commitment:
- Response time: < 48 hours on issues
- Security: See our SECURITY.md for vulnerability disclosure
- Breaking changes: 12-month notice before major versions, migration guides provided
- Roadmap: Public at GitHub Discussions
We believe in:
- Pragmatic error handling over academic FP
- Type safety without complexity
- Active maintenance and fast responses
Questions? Open an issue on GitHub or start a discussion.
License
MIT