npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fx-ts/fx

v0.0.5

Published

A functional result type for TypeScript

Readme

fx - Functional Result Type Library

A lightweight, type-safe Result type library for TypeScript that provides a functional approach to error handling.

Features

  • 🎯 Type-safe error handling with Result types
  • 🏷️ Error tagging for selective error handling
  • Async support with ResultAsync
  • 🔧 Functional transformations (map, flatMap, andThen)
  • 🎲 Combinators for working with multiple Results
  • 🛡️ Safe execution with tryCatch utilities
  • 📦 Zero dependencies - pure TypeScript

Installation

npm install @fx-ts/fx

Quick Start

import { fx } from 'fx';

// Create a function that might fail
const divide = fx.fn((a: number, b: number) => {
  if (b === 0) {
    return fx.err(new Error('Division by zero'));
  }
  return fx.ok(a / b);
});

// Use the function
const result = divide(10, 2);
// result: [null, 5] (success)

const errorResult = divide(10, 0);
// errorResult: [Error: Division by zero, null] (error)

// Transform results
const doubled = fx.map(result, x => x * 2);
// doubled: [null, 10]

// Chain operations
const chained = fx.andThen(result, x => {
  if (x > 3) {
    return fx.ok(`Big number: ${x}`);
  }
  return fx.err(new Error('Number too small'));
});
// chained: [null, "Big number: 5"]

Core Concepts

Result Type

A Result is a tuple [error, value] where:

  • [null, value] represents success
  • [error, null] represents failure
type Result<R, E> = [null, R] | [E, null];
type ResultAsync<R, E> = Promise<Result<R, E>>;

Creating Results

import { fx } from 'fx';

// Success
const success = fx.ok(42);
// [null, 42]

// Error
const error = fx.err(new Error('Something went wrong'));
// [Error: Something went wrong, null]

Safe Function Execution

import { fx } from 'fx';

// Wrap any function (sync or async)
const safeFunction = fx.fn(() => {
  // This function might throw
  const random = Math.random();
  if (random < 0.5) {
    throw new Error('Random failure');
  }
  return random;
});

const result = safeFunction();
// Always returns a Result, never throws

API Reference

Core Functions

fx.ok(value)

Creates a success Result.

fx.err(error)

Creates an error Result.

fx.fn(function)

Wraps a function to return Results instead of throwing.

Transformations

fx.map(result, fn)

Transform the success value of a Result.

const result = fx.ok(5);
const doubled = fx.map(result, x => x * 2);
// [null, 10]

fx.andThen(result, fn)

Chain operations on a Result (flatMap).

const result = fx.ok(5);
const chained = fx.andThen(result, x => {
  if (x > 3) return fx.ok(x * 2);
  return fx.err(new Error('Too small'));
});

fx.mapError(result, fn)

Transform the error of a Result.

const result = fx.err(new Error('Original error'));
const transformed = fx.mapError(result, err => `Custom: ${err.message}`);
// ["Custom: Original error", null]

fx.orElse(result, fn)

Provide a fallback when Result is an error.

const result = fx.err(new Error('Failed'));
const fallback = fx.orElse(result, err => fx.ok('Default value'));
// [null, "Default value"]

Unwrapping

fx.unwrap(result)

Extract the value or throw the error.

const result = fx.ok(42);
const value = fx.unwrap(result); // 42

const errorResult = fx.err(new Error('Oops'));
const value = fx.unwrap(errorResult); // throws Error: Oops

fx.unwrapOr(result, defaultValue)

Extract the value or return a default.

const result = fx.err(new Error('Failed'));
const value = fx.unwrapOr(result, 0); // 0

fx.unwrapOrElse(result, fn)

Extract the value or compute a default.

const result = fx.err(new Error('Failed'));
const value = fx.unwrapOrElse(result, err => err.message.length); // 6

Type Guards

fx.isOk(result)

Check if Result is success.

fx.isErr(result)

Check if Result is error.

Error Tagging

Error tagging allows you to categorize errors and handle them selectively in your pipelines.

fx.tagError(tag, error)

Create a tagged error.

const taggedError = fx.tagError("VALIDATION_ERROR", "Invalid input");
// { tag: "VALIDATION_ERROR", error: "Invalid input" }

fx.errTagged(tag, error)

Create a Result with a tagged error.

const result = fx.errTagged("NETWORK_ERROR", "Connection failed");
// [{ tag: "NETWORK_ERROR", error: "Connection failed" }, null]

fx.catchByTag(result, tag, handler)

Catch errors with a specific tag and handle them.

const result = fx.errTagged("VALIDATION_ERROR", "Invalid email");

const handled = fx.catchByTag(
  result,
  "VALIDATION_ERROR",
  (error) => fx.ok(`Handled: ${error}`)
);
// [null, "Handled: Invalid email"]

fx.catchByTags(result, tags, handler)

Catch errors with any of the specified tags.

const result = fx.errTagged("NETWORK_ERROR", "Connection failed");

const handled = fx.catchByTags(
  result,
  ["VALIDATION_ERROR", "NETWORK_ERROR"],
  (error) => fx.ok(`Handled: ${error}`)
);
// [null, "Handled: Connection failed"]

fx.hasTag(error, tag)

Check if an error has a specific tag.

const taggedError = fx.tagError("VALIDATION_ERROR", "Invalid input");
const hasTag = fx.hasTag(taggedError, "VALIDATION_ERROR"); // true

Combinators

fx.all(results)

Combine multiple Results. Returns success only if all are successful.

const results = [fx.ok(1), fx.ok(2), fx.ok(3)];
const combined = fx.all(results);
// [null, [1, 2, 3]]

fx.any(results)

Return the first successful Result, or the last error if all fail.

fx.zip(result1, result2)

Combine two Results into a tuple.

const result1 = fx.ok(1);
const result2 = fx.ok('hello');
const zipped = fx.zip(result1, result2);
// [null, [1, "hello"]]

fx.tryCatch(fn)

Execute a function and return a Result.

const result = fx.tryCatch(() => {
  const value = JSON.parse('invalid json');
  return value;
});
// [SyntaxError: Unexpected token i in JSON at position 0, null]

Async Support

All functions work with async Results:

import { fx } from 'fx';

const asyncResult = fx.fn(async () => {
  const response = await fetch('/api/data');
  if (!response.ok) {
    return fx.err(new Error('API failed'));
  }
  return fx.ok(await response.json());
});

const transformed = await fx.mapAsync(asyncResult(), data => data.name);

Best Practices

  1. Use Results for expected errors - Don't use Results for unexpected errors (bugs)
  2. Chain operations - Use fx.andThen to build complex workflows
  3. Handle errors explicitly - Use fx.mapError and fx.orElse to handle errors gracefully
  4. Use combinators - Use fx.all, fx.zip for working with multiple Results
  5. Prefer unwrapOr over unwrap - Avoid throwing in production code

Examples

API Call with Error Handling

import { fx } from 'fx';

const fetchUser = fx.fn(async (id: number) => {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    return fx.err(new Error(`HTTP ${response.status}`));
  }
  return fx.ok(await response.json());
});

const getUserName = fx.andThen(fetchUser(123), user => {
  if (!user.name) {
    return fx.err(new Error('User has no name'));
  }
  return fx.ok(user.name);
});

const result = await getUserName();

Validation Pipeline

import { fx } from 'fx';

const validateEmail = (email: string) => {
  if (!email.includes('@')) {
    return fx.err(new Error('Invalid email'));
  }
  return fx.ok(email);
};

const validateAge = (age: number) => {
  if (age < 18) {
    return fx.err(new Error('Too young'));
  }
  return fx.ok(age);
};

const validateUser = (email: string, age: number) => {
  const emailResult = validateEmail(email);
  const ageResult = validateAge(age);
  
  return fx.all([emailResult, ageResult]);
};

const result = validateUser('[email protected]', 25);
// [null, ["[email protected]", 25]]

Error Tagging Pipeline

import { fx } from 'fx';

const validateInput = (input: string) => {
  if (!input) return fx.errTagged("VALIDATION_ERROR", "Input is required");
  if (input.length < 3) return fx.errTagged("VALIDATION_ERROR", "Input too short");
  return fx.ok(input);
};

const processData = (input: string) => {
  if (input === "error") return fx.errTagged("PROCESSING_ERROR", "Failed to process");
  return fx.ok(input.toUpperCase());
};

const saveData = (data: string) => {
  if (data === "ERROR") return fx.errTagged("DATABASE_ERROR", "Failed to save");
  return fx.ok(`Saved: ${data}`);
};

// Pipeline with selective error handling
const pipeline = (input: string) => {
  const step1 = validateInput(input);
  
  const step2 = fx.catchByTag(
    step1,
    "VALIDATION_ERROR",
    (error) => fx.ok(`Validation failed: ${error}`)
  );
  
  const step3 = processData(fx.unwrap(step2));
  
  const step4 = fx.catchByTag(
    step3,
    "PROCESSING_ERROR",
    (error) => fx.ok(`Processing failed: ${error}`)
  );
  
  return saveData(fx.unwrap(step4));
};

const result = pipeline("hello");
// [null, "Saved: HELLO"]

const errorResult = pipeline("error");
// [null, "Saved: Processing failed: Failed to process"]

License

MIT