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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@pjelinski/try-catch

v2.0.0

Published

A utility for handling errors in both sync and async functions with less boilerplate.

Downloads

423

Readme

Overview

This package provides a utility function called tryCatch which simplifies error handling by allowing you to specify conditions under which errors are caught. It also provides a set of pre-defined catch conditions and a factory function to create custom catch conditions based on class instances.

Github Page

https://github.com/piotr-jelinski/try-catch

Installation

npm install @pjelinski/try-catch

Problem Statement

In JavaScript and TypeScript, managing error handling can become complex, especially when dealing with different error types and asynchronous operations. Standard try...catch blocks do not offer fine-grained control over which errors should be caught and handled versus which should propagate. This often leads to overly broad catch blocks or verbose error-checking logic within catch statements.

Furthermore, not all thrown values are instances of Error. JavaScript allows for the throwing of any value, including primitive types like strings, numbers, booleans, or even custom class instances. This can lead to unexpected behavior and errors slipping through unnoticed, resulting in runtime issues that are hard to debug and track down.

Another issue arises with variable scoping and returning values from within try...catch...finally blocks.

Example:

let something: SomeType;
try {
  // Declaring `something` here would limit its scope to the try block.
  // Execute some code that might throw an exception.
  something = someFunctionThatMayThrow();

  // If a return statement is placed here, and there's also a return statement in the finally block,
  // the return from this section will be overridden by the return in the finally block.
} catch (e: unknown) {
  // Handle the error; if `something` is declared within the try block, it won’t be accessible here.
  console.error(e);
} finally {
  // Perform cleanup or final actions.
  // If `something` was declared within the try block, it won’t be accessible here.
  // Also, if there’s a return statement in this block, it overrides any return in the try block.
}

// Returning here is possible, and `something` is available only because it's declared outside the try-catch block.

Key Points:

  • Declaring variables inside the try block limits their scope to the try block, making them unavailable in catch or finally blocks.
  • Return statements inside a finally block can override return statements in the try block, which can cause unexpected control flow behavior.
  • To ensure variables are accessible in all blocks (try, catch, and finally), you must declare them outside the try block.

This nuanced behavior often leads to confusing code and unexpected bugs, especially in scenarios involving asynchronous operations, cleanup logic, or complex return flows.

Goals of the Package

The goal of this package is to provide a more structured and precise way of handling errors by:

  • Allowing the definition of custom catch conditions to match specific error types.
  • Providing built-in conditions to catch common thrown types, including arrays, numbers, strings, and plain objects.
  • Supporting a factory method to easily create conditions for class-based custom errors.
  • Offering a utility function tryCatch that catches errors based on the provided conditions, making error handling more predictable and reducing unnecessary code clutter in catch blocks.
  • Simplifying the handling of values returned from try-catch blocks and reducing scoping issues for variables shared between try, catch, and finally sections.

This approach helps you focus on handling only the errors that are relevant to your logic while rethrowing other unexpected errors. It simplifies the process of matching and processing errors, both in synchronous and asynchronous contexts, and provides a consistent error-catching strategy throughout your codebase.

Example:

const [error, something] = tryCatch(() => {
  // this is what standard try block would contain
  // some code that can throw
  return somevalue;
});

if (error) {
  // this is what standard catch block would contain
  console.error(e);
  // we can return here and stop code from executing
}

// this is what standard finally block would contain
// handle something and/or return it

Exported Types

export type CatchCondition = (error: unknown) => boolean;
export type TryCatchResult<T> = [undefined, T] | [unknown];

Exported Catch Conditions

The package includes the following built-in CatchCondition functions:

  • isArrayThrown: Checks if the thrown error is an array.
  • isBigIntThrown: Checks if the thrown error is a BigInt.
  • isBooleanThrown: Checks if the thrown error is a boolean.
  • isErrorThrown: Checks if the thrown error is a plain Error object (i.e., not a custom error class).
  • isFunctionThrown: Checks if the thrown error is a function.
  • isInstanceOfErrorThrown: Checks if the thrown error is an instance of Error or any class extending Error.
  • isNullThrown: Checks if the thrown error is null.
  • isNumberThrown: Checks if the thrown error is a number.
  • isObjectThrown: Checks if the thrown error is a plain object (i.e., not an instance of a class).
  • isStringThrown: Checks if the thrown error is a string.
  • isSymbolThrown: Checks if the thrown error is a symbol.
  • isTypeOfObjectThrown: Checks if the thrown error is an object of any type, excluding null.
  • isUndefinedThrown: Checks if the thrown error is undefined.

Additionally, you can create custom catch conditions using the following factory function:

  • createCatchCondition: Creates a CatchCondition that checks if the thrown error is an instance of a specified class.

tryCatch Function

Function Signature

export default function tryCatch<T>(
  fn: () => Promise<T>,
  catchConditions?: CatchCondition[],
): Promise<TryCatchResult<T>>;

export default function tryCatch<T>(
  fn: () => T,
  catchConditions?: CatchCondition[],
): TryCatchResult<T>;

Function Description

The tryCatch function accepts a synchronous or asynchronous function fn, executes it, and catches any thrown errors based on the provided catchConditions. If no catchConditions are provided, all thrown errors will be caught.

The function returns a result in the form of a tuple:

  • If the function fn executes successfully, the returned tuple is [undefined, result], where result is the value returned by fn.
  • If an error is caught, the returned tuple is [error].

If an error does not match any of the provided catchConditions, it is rethrown.

Example Usage

Catching All kinds of Error

import tryCatch from '@pjelinski/try-catch';

class CustomError extends Error {}

const [error, result] = tryCatch(() => {
  if (Math.random() < 0.5) {
    throw new CustomError('Something went wrong');
  }

  return 'Success!';
});

if (error) {
  console.error('Caught CustomError', error);
  // stop code from executing
}
// handle result

const [error, result] = tryCatch(() => {
  if (Math.random() < 0.5) {
    throw new Error('Something went wrong');
  }

  return 'Success!';
});
if (error) {
  console.error('Caught Error', error);
  // stop code from executing
}
// handle result

const [error, result] = tryCatch(() => {
  if (Math.random() < 0.5) {
    throw 'Error';
  }

  return 'Success!';
});
if (error) {
  console.error('Caught Error', error);
  // stop code from executing
}
// handle result

class MyClass = {}

const [error, result] = tryCatch(() => {
  if (Math.random() < 0.5) {
    throw new MyClass();
  }

  return 'Success!';
});
if (error) {
  console.error('Caught MyClass', error);
  // stop code from executing
}
// handle result

Catch specific type of error only

import tryCatch, {
  isErrorThrown,
  isInstanceOfErrorThrown,
} from '@pjelinski/try-catch';

class CustomError extends Error {}

try {
  tryCatch(() => {
    throw new CustomError('Something went wrong');
  }, [isErrorThrown]);
} catch (error: unknown) {
  console.error('CustomError was not caught but thrown instead', error);
}

const [error] = tryCatch(() => {
  throw new Error('Something went wrong');
}, [isErrorThrown]);
if (error) {
  console.error('Caught Error', error);
}

const [error] = tryCatch(() => {
  throw new CustomError('Something went wrong');
}, [isInstanceOfErrorThrown]);
if (error) {
  console.error('Caught CustomError', error);
}

Factory Function Example

import { createCatchCondition } from '@pjelinski/try-catch';

class MyCustomClass {}

const isMyCustomClassThrown = createCatchCondition(MyCustomClass);

const [error] = tryCatch(() => {
  throw new MyCustomClass();
}, [isMyCustomClassThrown]);

if (error) {
  console.log('Caught an instance of MyCustomClass');
}

try {
  tryCatch(() => {
    throw new Error('Something went wrong');
  }, [isMyCustomClassThrown]);
} catch (error: unknown) {
  console.error('Error was not caught but thrown instead', error);
}

Catching async function call

const [error, result] = await tryCatch(async () => {
  return await someAsyncFunctionThatMayThrow();
});

if (error) {
  console.log('Caught an error', error);
}

// handle result

Changelog

v2.x

  • Updated error-catching approach: The implementation now uses CatchCondition[] instead of the previous ErrorType[], enabling more flexible and granular error matching.
  • New built-in catch conditions: Added conditions for catching thrown values such as arrays, primitive types (number, boolean, string, etc.), functions, and null/undefined.
  • Custom error matching: Introduced createCatchCondition to easily create custom conditions for class-based errors.
  • Improved return handling: Return values are now accessible consistently across all blocks (try, catch, and finally) due to external variable declaration.
  • Code cleanup and refactoring: The codebase was restructured for enhanced readability and maintainability.

Version History

v1.x

  • Initial implementation using ErrorType[] to catch errors of specific types.
  • Supported both synchronous and asynchronous functions with basic error handling.
  • Lacked flexibility in catching non-Error values and primitive types.
  • Scoping issues in error handling and variable declaration inside try-catch-finally blocks.

v2.x

  • Replaced ErrorType[] with CatchCondition[], offering more precise error handling based on custom conditions.
  • Added built-in conditions to catch arrays, primitive values, functions, null/undefined, and more.
  • Introduced createCatchCondition utility for creating custom class-based error-catching logic.
  • Simplified error handling by ensuring consistent access to return values across all blocks.
  • Enhanced code structure and modularity for better maintainability.

Breaking Changes

The new implementation of tryCatch replaces the old ErrorType[] approach with a more flexible array of CatchCondition[] functions. This change introduces the following key improvements:

  1. Enhanced Error Matching Flexibility: Catch conditions allow fine-grained control to handle any type of thrown value, including primitives, arrays, plain objects, and custom classes. This eliminates the previous limitation of handling only specific Error classes and makes error matching more versatile.

  2. Simplified API and Improved Consistency: By using catch conditions instead of error classes, the API is streamlined, reducing complexity while improving predictability. This change ensures that error handling is consistently applied across various thrown types without requiring additional logic or manual error checks.

These changes result in a more powerful and user-friendly error handling experience, allowing developers to create customized conditions for any thrown value while maintaining a clear and concise API.

License

This project is licensed under the MIT License.