@deessejs/fp

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 contextErrors have rich context
Types lieTypes tell the truth
Nested messFlat, composable chains
Silent failures possibleCompiler enforces handling
Unknown failure modesExplicit 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); // 5

Now the type signature Result<number, string> tells you: "this returns a number, but it might fail with a string error."

Why @deessejs/fp?

FeatureBenefit
Zero runtime dependenciesLightweight bundle, no bloat
Full TypeScript supportComplete type inference
Composable APIChain operations with map, flatMap
Functional patternsmap, flatMap, tap, match, and more
Multiple typesResult, Maybe, Try, AsyncResult
Error SystemPython-inspired error handling with notes, cause chains, and groups

Core Types

Our library provides multiple types for different scenarios:

Getting Started

Install the library:

pnpm add @deessejs/fp

Then 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:

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

On this page