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

typescript-errors

v1.1.1

Published

Minimal type safe error handling for TypeScript

Downloads

29

Readme

typescript-errors

TypeScript library for creating and handling type safe errors without the use of throw statements.

Why?

There are quite a few other packages out there that solve the same problem as this one, but they are all a bit complex and hard to understand without a deep grasp of typescript or Functional Programming.

This package is designed so that the highly opinionated pattern is as simple to understand as possible while still being type safe and easy to use.

The Problem

Throwing errors in TypeScript makes it impossible for typescript to infer that a function may throw. This package aims to solve that problem while making it easier to split control flow based on the type of error that bubbles up from underlying code.

The Solution

Stop throwing errors. Instead, we use a few simple functions to wrap our code and handle errors in a type safe way. When you call a function that may fail, you can check the result to see if it is an error or not. If you try to access data returned from a function that may fail, you will be forced to handle the error case first. This leads to a much more robust and type safe codebase.

Under the hood we still use an extended Error class so that the error can be used like a normal error if needed.

How

The Functions

The pattern is based around 2 main function types:

May fail functions

These are helper functions that wrap your code and return an error if something goes wrong. There are 3 convenience functions for these:

  • mayFail - A function that takes a function and returns an error if the function throws.
  • promiseMayFail - A function that takes a promise and returns an error if the promise rejects.
  • promiseMapMayFail - A function that takes an array of promises and returns an array of errors if any of the promises reject.

Is Error

The isError() function is a type guard that takes a value and returns true if the value is an error. It accepts an optional error code to check against so that you can handle errors differently based on the error code that bubbles up.

The throwIfError() function will throw the error if the value is a typescript error. It accepts an optional error code to throw so that you can handle errors differently based on the error code that bubbles up. There are times when you actually want to throw an error and this is the function to use.

Utility functions

The newError() function creates a new typescript error class. This is useful if you need to create an error without calling a mayFail function.

Basic Usage

Initialize the package using your error codes and messages

import type { TSErrorDefinition } from 'typescript-errors';
import { init } from 'typescript-errors';

// Define your error codes and messages
const TS_ERRORS = {
  'test:error': {
    message: 'An error occurred',
    statusCode: 400, // Optional, defaults to 500
  },
} as const satisfies TSErrorDefinition;

const tsErr = init(TS_ERRORS);

Run something that may fail

const result = tsErr.mayFail(() => {
  const data = JSON.parse(someJson);
  return {
    id: data.id,
    name: data.name,
  };
}, 'test:error');

if (tsErr.isError(result, 'test:error')) {
  // Handle a specific error differently than other errors
  console.error(result.message);
  return result; // bubble up the error
} else if (tsErr.isError(result)) {
  // Handle any other error
  console.error(result.message);
  return result; // bubble up the error
}

// Typescript will correctly infer the result type and throw an error if you try
// to access properties without checking the error type first
console.log(result);

Run something async that may fail

const result = await tsErr.promiseMayFail(
  fetch('https://api.example.com'),
  'test:error',
);

if (tsErr.isError(result, 'test:error')) {
  // Handle a specific error differently than other errors
  console.error(result.message);
  return result; // bubble up the error
} else if (tsErr.isError(result)) {
  // Handle any other error
  console.error(result.message);
  return result; // bubble up the error
}

// Typescript will correctly infer the result type and throw an error if you try
// to access properties without checking the error type first
console.log(result);

Run something async that may fail and return a promise

const ids = [1, 2, 3];
const result = await tsErr.promiseMapMayFail(
  ids.map((n) => fetch(`https://api.example.com/${n}`)),
  'test:error',
);

if (tsErr.isError(result, 'test:error')) {
  // Handle a specific error differently than other errors
  console.error(result.message);
  return result; // bubble up the error
} else if (tsErr.isError(result)) {
  // Handle any other error
  console.error(result.message);
  console.dir(result.meta.error, { depth: null }); // Can access error information for items that errored
  console.dir(result.meta.results, { depth: null }); // Can access results for items that succeeded
  return result; // bubble up the error
}

// Typescript will correctly infer the result type and throw an error if you try
// to access properties without checking the error type first
console.log(result);

Sometimes you actually WANT to throw (like in a top level remix route)

const result = await tsErr.throwIfError(
  tsErr.promiseMayFail(Promise.reject(new Error('test')), 'test:error'),
);

Error Bubbling

If you use a mayFail function and the underlying function returns a typescript error, that error will be returned instead of the error you provided. The error you provided will only be returned if the underlying function throws.

const TS_ERRORS = {
  'test:error': {
    message: 'Test error',
    statusCode: 400,
  },
  'test:error:somethingElse': {
    message: 'Something else went wrong',
    statusCode: 403,
  },
} as const satisfies TSErrorDefinition;

const tsErr = init(TS_ERRORS);

const result = tsErr.mayFail(() => {
  return tsErr.newError({ code: 'test:error:somethingElse' });
}, 'test:error');

console.dir(result, { depth: null }); // { code: 'test:error:somethingElse', message: 'Something else went wrong', statusCode: 403 }

Dynamic error messages

The message for an error can be a function. This allows you to create errors with dynamic messages.

const TS_ERRORS = {
  'test:error:functionMessage': {
    message: (args: TSErrorDefinitionMessageFnArgs) =>
      `Test error from function ${String(args.code)} ${String(args.meta?.customText)}`,
    statusCode: 400,
  },
} as const satisfies TSErrorDefinition;

const tsErr = init(TS_ERRORS);

const result = tsErr.mayFail(() => {
  throw tsErr.newError({
    code: 'test:error:functionMessage',
    meta: { customText: 'custom text' },
  });
}, 'test:error:functionMessage');

console.error(result.message); // "Test error from function test:error:functionMessage custom text"

The TSError object

The TSError object is a custom error class that extends the native Error class. It is used to create errors that can be used like a normal error if needed.

It includes the following properties:

  • cause: The original error that caused the error to be thrown which includes the stack trace
  • code: The error code
  • message: The error message
  • statusCode: The HTTP status code
  • meta: The error meta data

The TSError object is not intended to be used directly. Instead, you should use the newError function to create errors if you need to create an error without calling a mayFail function.

const error = tsErr.newError({
  code: 'test:error',
  message: 'Test error',
  statusCode: 400,
  meta: { customText: 'custom text' },
});