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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@julian-i/try-error

v0.1.11

Published

Elegant tuple-based error handling utilities for TypeScript, inspired by Rust's Result<T, E> pattern.

Readme

try-error

Elegant tuple-based error handling utilities for TypeScript, inspired by Rust's Result<T, E> pattern.

🔍 Philosophy

This utility module encourages a pattern of handling asynchronous and synchronous operations through predictable tuples, inspired by Rust's Result<T, E> pattern.

Our approach complements existing error handling strategies — exceptions are still allowed and meaningful when appropriate. This library provides an alternative pattern where thrown errors are captured and surfaced as the second value of a result tuple, enabling safe branching and structured error handling while maintaining the flexibility to throw when needed.

Similar to Rust's Result<T, E>, we model results as [T, null] or [null, Error], enabling developers to destructure outcomes, check for failure via helpers, and respond clearly. This pattern ensures callers can handle errors gracefully without unexpected crashes, while still allowing you to throw exceptions when they make sense for your use case.

Whether you prefer traditional try/catch blocks, functional Result types, or a hybrid approach, this library gives you the tools to handle errors in a way that fits your coding style and project needs.

🚀 Install

npm install @julian-i/try-error

🔧 Usage Guidelines

  • tryFn(fn) - Use when you want a reusable wrapper for a function (sync or async)
  • tryPromise(promise) - Use for one-off async expressions
  • tryCatch(() => syncWork()) - Use for one-off synchronous expressions
  • tryMap(inputs, fn) - Use to apply a safe function across an array
  • tryPipe(...fns) - Use to compose a pipeline of safe steps that short-circuit on failure
  • isFailure, isTrySuccess, getFailureReason - Use for ergonomic result handling

📖 Examples

Basic Usage

import {
  tryFn,
  tryPromise,
  tryCatch,
  isSuccess,
  isErrorOrNoData,
  getFailureReason,
} from "@julian-i/try-error";

// Wrapping a function for reuse
const safeFetch = tryFn(async (url: string) => {
  const response = await fetch(url);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
});

// Using the wrapped function
const [data, error] = await safeFetch("https://api.example.com/data");
if (isSuccess([data, error])) {
  console.log("Success:", data);
} else {
  console.error("Failed:", getFailureReason([data, error]));
}

// One-off promise handling
const [result, err] = await tryPromise(fetch("https://api.example.com/data"));

// Synchronous error handling
const [value, error] = tryCatch(() => {
  return JSON.parse("invalid json");
});

// Validating API responses
const [apiResponse, apiError] = await apiCall();
const validationError = isErrorOrNoData([apiResponse, apiError], "success");
if (validationError) {
  console.error("API validation failed:", validationError.message);
}

Array Processing

import { tryMap } from "@julian-i/try-error";

const urls = ["https://api1.com", "https://api2.com", "https://api3.com"];

const results = await tryMap(urls, async (url) => {
  const response = await fetch(url);
  return response.json();
});

// results is TryResult<Data>[] - each item is either [data, null] or [null, Error]
results.forEach(([data, error], index) => {
  if (error) {
    console.error(`Failed to fetch ${urls[index]}:`, error.message);
  } else {
    console.log(`Successfully fetched ${urls[index]}:`, data);
  }
});

Pipeline Composition

import { tryPipe } from "@julian-i/try-error";

const processUserData = tryPipe(
  async (userId: string) => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  },
  (userData) => {
    if (!userData.email) throw new Error("User has no email");
    return userData;
  },
  async (userData) => {
    const processed = await processUser(userData);
    return { ...userData, processed };
  }
);

const [result, error] = await processUserData("123");
if (error) {
  console.error("Pipeline failed:", error.message);
} else {
  console.log("Pipeline succeeded:", result);
}

Advanced Error Handling

import { isFailure, getFailureReason } from "@julian-i/try-error";

// Check for various failure conditions
const [apiResult, apiError] = await safeApiCall();

if (isFailure([apiResult, apiError])) {
  // This handles: null/undefined results, thrown errors, or {success: false} objects
  console.error("Operation failed:", getFailureReason([apiResult, apiError]));
}

// Custom status key
const [customResult, customError] = await customApiCall();
if (isFailure([customResult, customError], "status")) {
  // Checks for {status: false} in addition to other failure conditions
  console.error("Custom operation failed");
}

📚 API Reference

Types

type TryResult<T> = [T, null] | [null, Error];

Core Functions

tryFn<TArgs, TReturn>(fn)

Creates a reusable wrapper function that returns TryResult<TReturn>.

function tryFn<TArgs extends any[], TReturn>(
  fn: (...args: TArgs) => Promise<TReturn> | TReturn
): (...args: TArgs) => Promise<TryResult<TReturn>>;

tryPromise<T>(promise)

Wraps a promise to return a TryResult<T>.

function tryPromise<T>(promise: Promise<T>): Promise<TryResult<T>>;

tryCatch<T>(fn)

Wraps a synchronous function to return a TryResult<T>.

function tryCatch<T>(fn: () => T): TryResult<T>;

Utility Functions

isSuccess<T>(result)

Type guard to check if a result is successful.

function isSuccess<T>(result: TryResult<T>): result is [T, null];

isError<T>(result)

Type guard to check if a result is an error.

function isError<T>(result: TryResult<T>): result is [null, Error];

isErrorOrNoData<T>(result, statusKey?)

Checks for error conditions or missing data, returning an Error instance or undefined.

function isErrorOrNoData<T>(
  result: TryResult<T>,
  statusKey?: string
): Error | undefined;

This function is useful for validating API responses that might have error states or missing data:

// Check for errors or null data
const [data, error] = await fetchUserData();
const validationError = isErrorOrNoData([data, error]);
if (validationError) {
  console.error("Validation failed:", validationError.message);
  return;
}

// Check for errors or failed status
const [response, responseError] = await apiCall();
const statusError = isErrorOrNoData([response, responseError], "success");
if (statusError) {
  console.error("API call failed:", statusError.message);
  return;
}

isFailure<T>(result, statusKey?)

Checks for various failure conditions including custom status keys.

function isFailure<T extends Record<string, any>>(
  result: TryResult<T>,
  statusKey?: keyof T | null
): boolean;

getFailureReason<T>(result)

Extracts a human-readable failure reason from a result.

function getFailureReason<T extends { message?: string }>(
  result: TryResult<T>
): string | undefined;

Collection Functions

tryMap<T, R>(inputs, fn)

Applies a safe function across an array of inputs.

function tryMap<T, R>(
  inputs: T[],
  fn: (item: T, index: number) => Promise<R> | R
): Promise<TryResult<R>[]>;

Composition Functions

tryPipe<TArgs, TStep, TResult>(...fns)

Composes a pipeline of functions that short-circuits on failure.

function tryPipe<TArgs extends any[], TStep, TResult>(
  ...fns: [
    (...args: TArgs) => Promise<TStep> | TStep,
    ...((input: any) => Promise<any> | any)[]
  ]
): (...args: TArgs) => Promise<TryResult<TResult>>;

Helper Functions

ensureError(value)

Ensures a value is converted to an Error instance.

function ensureError(value: unknown): Error;

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT