@deessejs/fp

Comparison

How @deessejs/fp compares to other error handling libraries

How does @deessejs/fp compare to other error handling libraries in the TypeScript ecosystem?

Quick Comparison

Feature@deessejs/fpfp-tsneverthrowts-results
Size (min+gzip)~5KB~40KB~6KB~8KB
Runtime deps01 (fp-ts)00
Types520+33
Async supportYesYesNoNo
Exhaustiveness checkingYesYesPartialYes

@deessejs/fp vs fp-ts

fp-ts is a comprehensive functional programming library with many types and utilities.

fp-ts:

  • 40KB bundle size (8x larger)
  • Steep learning curve
  • Rich ecosystem of abstractions
  • Requires FP knowledge

@deessejs/fp:

  • ~5KB (lightweight)
  • Simple, pragmatic API
  • Focused on error handling only
  • No FP jargon

Use fp-ts if you need the full ecosystem of functional programming abstractions. Use @deessejs/fp if you just want type-safe error handling.

@deessejs/fp vs neverthrow

neverthrow is a Result type library for TypeScript.

neverthrow:

  • Only Result type
  • No async support
  • Similar size (~6KB)

@deessejs/fp:

  • 4 types (Result, Maybe, Try, AsyncResult)
  • Built-in async handling
  • Conversion utilities between types
  • Slightly smaller

If you only need Result, neverthrow is a solid choice. @deessejs/fp gives you more types in a similar footprint.

@deessejs/fp vs ts-results

ts-results is another Result type implementation.

ts-results:

  • Only Result type
  • No Maybe or Try
  • Larger bundle (~8KB)

@deessejs/fp:

  • More types for different scenarios
  • Smaller bundle
  • Additional utilities (retry, sleep)

When to Choose @deessejs/fp

Choose @deessejs/fp when you want:

  • Lightweight - ~5KB, zero runtime deps
  • Comprehensive - 5 types for different error scenarios
  • Pragmatic - No functional programming knowledge required
  • TypeScript-first - Full type inference, no magic
  • Async-ready - Built-in AsyncResult for network operations

Migration from other libraries

From try/catch

Ready to make the switch? Here's the simplest example:

// try/catch
try {
  const data = JSON.parse(input);
} catch (e) {
  console.error('Parse failed:', e.message);
}

// @deessejs/fp
import { attempt } from '@deessejs/fp';

const result = attempt(() => JSON.parse(input));
result.match({
  onSuccess: (data) => console.log(data),
  onError: (e) => console.error('Parse failed:', e.message)
});

Read more about why to switch

From neverthrow

// neverthrow
import { ok, err, Result } from "neverthrow";

const divide = (a: number, b: number): Result<number, string> => {
  if (b === 0) return err("Division by zero");
  return ok(a / b);
};

// @deessejs/fp - almost identical!
import { ok, err, Result } from "@deessejs/fp";

const divide = (a: number, b: number): Result<number, string> => {
  if (b === 0) return err("Division by zero");
  return ok(a / b);
};

From fp-ts

// fp-ts
import { fromEither, tryCatch } from "fp-ts/TaskEither";

const parseJson = (json: string) =>
  tryCatch(
    () => JSON.parse(json),
    () => new Error("Parse failed")
  );

// @deessejs/fp - simpler!
import { attempt } from "@deessejs/fp";

const parseJson = (json: string) => attempt(() => JSON.parse(json));

From throwing exceptions

// Before
function divide(a: number, b: number): number {
  if (b === 0) throw new Error("Division by zero");
  return a / b;
}

// After @deessejs/fp
import { ok, err, Result } from "@deessejs/fp";

const divide = (a: number, b: number): Result<number, string> => {
  if (b === 0) return err("Division by zero");
  return ok(a / b);
};

See Also

On this page