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

@utilityjs/resolve-throwable

v2.0.0

Published

Executes a given asynchronous function and returns its result in a structured object, converting potential thrown errors into a safe `error` property.

Downloads

23

Readme

UtilityJS | resolveThrowable

Executes a given asynchronous function and returns its result in a structured object converting potential thrown errors into a safe error property. This prevents the application from crashing due to uncaught promise rejections and simplifies error handling in asynchronous code.

Features

  • Safe Error Handling: Converts thrown errors into structured result objects
  • No Try-Catch Needed: Eliminates the need for try-catch blocks
  • TypeScript Support: Full type safety with generic error types
  • Promise-based: Works with any async function
  • Discriminated Union: Type-safe result checking with data and error properties

Installation

npm install @utilityjs/resolve-throwable

or

pnpm add @utilityjs/resolve-throwable

Usage

Basic Example

import { resolveThrowable } from "@utilityjs/resolve-throwable";

const fetchData = async () => {
  const response = await fetch("/api/data");
  return response.json();
};

const result = await resolveThrowable(fetchData);

if (result.error) {
  console.error("Failed to fetch data:", result.error);
} else {
  console.log("Data:", result.data);
}

With Custom Error Types

import { resolveThrowable } from "@utilityjs/resolve-throwable";

class NetworkError extends Error {
  constructor(
    message: string,
    public statusCode: number,
  ) {
    super(message);
    this.name = "NetworkError";
  }
}

const fetchUser = async (id: string) => {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw new NetworkError("Failed to fetch user", response.status);
  }
  return response.json();
};

const result = await resolveThrowable<NetworkError, typeof fetchUser>(() =>
  fetchUser("123"),
);

if (result.error) {
  console.error(`Error ${result.error.statusCode}:`, result.error.message);
} else {
  console.log("User:", result.data);
}

Multiple Operations

import { resolveThrowable } from "@utilityjs/resolve-throwable";

async function loadUserData(userId: string) {
  const userResult = await resolveThrowable(() => fetchUser(userId));
  if (userResult.error) {
    return { error: "Failed to load user" };
  }

  const postsResult = await resolveThrowable(() =>
    fetchUserPosts(userResult.data.id),
  );
  if (postsResult.error) {
    return { error: "Failed to load posts" };
  }

  return {
    user: userResult.data,
    posts: postsResult.data,
  };
}

React Component Example

import { resolveThrowable } from "@utilityjs/resolve-throwable";
import { useState, useEffect } from "react";

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState(null);
  const [error, setError] = useState<Error | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const loadUser = async () => {
      setLoading(true);

      const result = await resolveThrowable(async () => {
        const response = await fetch(`/api/users/${userId}`);
        return response.json();
      });

      if (result.error) {
        setError(result.error);
      } else {
        setUser(result.data);
      }

      setLoading(false);
    };

    loadUser();
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>User: {user?.name}</div>;
}

Form Submission

import { resolveThrowable } from "@utilityjs/resolve-throwable";

async function handleSubmit(formData: FormData) {
  const result = await resolveThrowable(async () => {
    const response = await fetch("/api/submit", {
      method: "POST",
      body: formData,
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    return response.json();
  });

  if (result.error) {
    alert(`Submission failed: ${result.error.message}`);
    return false;
  }

  alert("Submission successful!");
  return true;
}

API

resolveThrowable<E, F>(throwableFn: F): ThrowableResult<Awaited<ReturnType<F>>, E>

Executes a given asynchronous function and returns its result in a structured object.

Type Parameters:

  • E extends Error - The type of error that might be thrown (defaults to Error)
  • F extends (...args: any[]) => any - The type of the async function

Parameters:

  • throwableFn: F - The asynchronous function to execute

Returns:

  • ThrowableResult<T, E> - A promise that resolves to either:
    • Success<T>: { data: T, error: null } when successful
    • Failure<E>: { data: null, error: E } when an error is thrown

Types

ThrowableResult<T, E>

type ThrowableResult<T, E extends Error = Error> = Promise<
  Success<T> | Failure<E>
>;

The unified return type that guarantees either data or error will be present.

Success<T>

type Success<T> = {
  data: T;
  error: null;
};

Represents a successful operation result.

Failure<E>

type Failure<E> = {
  data: null;
  error: E;
};

Represents a failed operation result.

Benefits

Type Safety:

  • TypeScript discriminated unions ensure type-safe error checking
  • No need for type assertions or unsafe casts

Cleaner Code:

  • Eliminates nested try-catch blocks
  • Makes error handling explicit and visible
  • Easier to compose multiple async operations

Predictable Behavior:

  • Always returns a structured result
  • No uncaught promise rejections
  • Consistent error handling pattern

Comparison with Try-Catch

Traditional approach:

try {
  const data = await fetchData();
  console.log(data);
} catch (error) {
  console.error(error);
}

With resolveThrowable:

const result = await resolveThrowable(fetchData);
if (result.error) {
  console.error(result.error);
} else {
  console.log(result.data);
}

Contributing

Read the contributing guide to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes.

License

This project is licensed under the terms of the MIT license.