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

try-to-result

v1.0.5

Published

Result Type with try method to catch errors

Readme

try-to-result Coverage Status npm version npm downloads GitHub license

A TypeScript-first emulation of the try‑operator proposed by proposal-try-operator, including a polyfill for the Result type.

Installation

npm install try-to-result

or

yarn add try-to-result

Usage

Wrap any synchronous or asynchronous computation in Result.try. It never throws – instead it returns a discriminated union you can safely destructure, like tuple.

Basic usage

import Result from 'try-to-result';

function div(a: number, b: number): number {
  if (b !== 0) return a / b;
  throw new Error('Division by zero');
}

const [ok, error, value] = Result.try(() => div(4, 2));

if (ok) {
  console.log(value * 2); // 4
} else {
  console.error('Failed:', error);
}

or the same but without creating a new function:

import Result from 'try-to-result';

const [ok, error, value] = await Result.try(div, 4, 2);

if (ok) {
  console.log(value * 2); // 4
} else {
  console.error('Failed:', error);
}

Promises and async functions

You can also use Result.try with promises and async functions:

import Result from 'try-to-result';

type User = { id: string; name: string };

async function fetchUser(id: string): Promise<User | null> {
  const [ok, error, res] = await Result.try(
    fetch(`https://api.example.com/users/${id}`),
  );

  if (!ok) {
    // no response received
    throw new Error(`Failed to fetch user: ${error}`, { cause: error });
  }

  if (res.status === 404) {
    // user not found
    return null;
  }

  if (res.status !== 200) {
    // handle other HTTP errors
    throw new Error(`HTTP error on fetching user: ${res.status}`);
  }

  const [okJson, jsonError, data] = await Result.try(res.json());

  if (!okJson) {
    // failed to parse JSON response
    throw new Error(`Failed to parse user data: ${jsonError}`, { cause: jsonError });
  }

  return data as User;
}

Accepting plain synchronous values

const result = Result.try(42);
// result = Result.ok(42)

Returning Results

Instead of exceptions, you can return a typed Result from your functions:

import Result from 'try-to-result';

type DivError = 
  | 'ERR_DIV_BY_ZERO' // x / 0, x != 0
  | 'ERR_IND_FORM';   // 0 / 0

function div(a: number, b: number): Result<number, DivError> {
  if (b !== 0) return Result.ok(a / b);
  if (a !== 0) return Result.error('ERR_DIV_BY_ZERO');
  return Result.error('ERR_IND_FORM');
}

const result = div(4, 0);

if (result.ok) {
  console.log(result.value);
} else {
  console.error(result.error); // 'ERR_DIV_BY_ZERO'
}

Returning Async Results

import Result from 'try-to-result';

type FetchError =
  | { code: 'NETWORK_ERROR', cause: unknown }
  | { code: 'HTTP_ERROR', status: number }
  | { code: 'PARSE_JSON_ERROR', cause: unknown }
  | { code: 'ABORTED' };

type FetchJsonResult<T> = Result<T, FetchError>;

async function fetchJson<T = unknown>(
  url: string,
  init?: RequestInit,
): Promise<FetchJsonResult<T>> {
  const [ok, error, res] = await Result.try(fetch(url, init));

  if (!ok) {
    // no response received
    return Result.error(
      (error as any)?.name === 'AbortError'
        ? { code: 'ABORTED' }
        : { code: 'NETWORK_ERROR', cause: error },
    );
  }

  if (!res.ok) {
    // handle HTTP errors
    return Result.error({ code: 'HTTP_ERROR', status: res.status });
  }

  const [okJson, jsonError, data] = await Result.try(res.json());

  if (!okJson) {
    // failed to parse JSON response
    return Result.error({ code: 'PARSE_JSON_ERROR', cause: jsonError });
  }

  return Result.ok(data as T);
}

type User = { id: string; name: string; companyId: string };

export async function fetchUser(id: string): Promise<User | null> {
  const [ok, error, data] = await fetchJson(
    `https://api.example.com/users/${id}`,
  );

  if (ok) return data as User;

  if (error.code === 'ABORTED') {
    return null;
  }

  if (error.code !== 'HTTP_ERROR') {
    throw new Error(`Failed to fetch user: ${error.code}`, {
      cause: error.cause,
    });
  }

  if (error.status === 404) {
    return null;
  }

  throw new Error(`HTTP error on fetching user: ${error.status}`);
}

In the above example, fetchJson returns a Promise resolving to a Result and delegates error handling to the caller. Thanks to return value of fetchJson is strictly typed, the fetchUser function can confidently handle all error cases.

Usage of typed results with Result.do

Result.do makes control flow easier by letting you unpack results inline with a generator:

type DivError = 
  | 'ERR_DIV_BY_ZERO' // x / 0, x != 0
  | 'ERR_IND_FORM';   // 0 / 0

function div(a: number, b: number): Result<number, DivError> {
  if (b !== 0) return Result.ok(a / b);
  if (a !== 0) return Result.error('ERR_DIV_BY_ZERO');
  return Result.error('ERR_IND_FORM');
}

type SqrError = 'ERR_NEGATIVE_SQUARE_ROOT';

function sqrt(x: number): Result<number, SqrError> {
  if (x < 0) return Result.error('ERR_NEGATIVE_SQUARE_ROOT');
  return Result.ok(Math.sqrt(x));
}

const result = Result.do(function* (_) {
  const x = yield* _(div(4, 2)); // unpack Result from div
  const y = yield* _(sqrt(x));    // unpack Result from sqrt
  return y * 2;                  // return final value
});

In this example, the yield* _(result) unpacks the Result values, only if they are Result.ok. If the result is Result.error, the control flow stops and returns that error. The function Result.do is in charge of returning yielded values as Result.error.

For async workflows, use an async function* and await:

type User = { id: string; name: string, companyId: string };

const result = await Result.do(async function* (_) {
  const user = yield* _(await fetchJson<User>('/users/1'));
  const company = yield* _(await fetchJson<Company>(
    `/companies/${user.companyId}`
  ));

  return `${user.name} works at ${company.name}`;
});

console.log(result); // Result.ok('John Doe works at Example Corp')

Specification

| API | Description | | ------------------------------------ | --------------------------------------------------------------------------------------- | | Result.ok(value) | Creates a successful result. | | Result.ok() | Creates a void successful result. | | Result.error(error) | Creates an error result. | | Result.error() | Creates a void error result. | | Result.try(value) | Wraps a plain value in Result.ok. | | Result.try(promise) | Converts a Promise into Promise<Result<T>>. | | Result.try(fn, ...args) | Calls a function (sync or async) with arguments, capturing thrown errors or rejections. | | Result.collect(results) | Collects multiple results into a single result of array of values. Returns the first error if any result is an ErrorResult. | | Result.do(function*) | Structured error handling with generator-based flow. | | Result.do(async function*) | Structured error handling with async generator-based flow. |

Type Result

type Result<T, E = unknown> = ValueResult<T> | ErrorResult<E>;

Class Result

class Result {
  static ok(): ValueResult<void>;
  static ok<T>(value: T): ValueResult<T>;
  static error(): ErrorResult<void>;
  static error<E>(error: E): ErrorResult<E>;
  static try<T>(value: T): ValueResult<T>;
  static try<T>(fn: () => T): ValueResult<T>;
  static try<T>(promise: Promise<T>): Promise<ValueResult<T>>;
  static try<T>(fn: (...args: any[]) => T, ...args: any[]): ValueResult<T>;
  static collect<T, E>(results: Result<T, E>[]): Result<T[], E>;
  static do<T>(generator: (yield: <R>(result: Result<R>) => Result<R>) => Generator<Result<T>>): Result<T>;
  static do<T>(generator: (yield: <R>(result: Promise<Result<R>>) => Promise<Result<R>>) => AsyncGenerator<Result<T>>): Promise<Result<T>>;
}

The Result is not a constructor. So, you cannot use new Result(). Instead, use the static methods: Result.ok, Result.error to create results, and Result.try, Result.collect, and Result.do to work with them.

Result.ok: ValueResult<T>

Represents a successful value:

type ValueResult<T> = readonly [
  ok: true,
  error: undefined,
  value: T
] & { readonly ok: true; readonly value: T };

Constructed with:

Result.ok(value)

Result.error: ErrorResult<T>

Represents an error value:

type ErrorResult<E> = readonly [
  ok: false,
  error: E,
  value: undefined
] & { readonly ok: false; readonly error: E };

Constructed with:

Result.error(error)

Result.try

Wraps a value, function, or promise:

  • Result.try(value) → wraps a plain value
  • Result.try(fn) → executes a function and captures exceptions
  • Result.try(promise) → converts a Promise into Promise<Result<T>>
  • Result.try(fn, ...args) → calls a function with given arguments and wraps the result

Returns:

  • Result<T> for synchronous values
  • Promise<Result<T>> for async functions/promises

Alternative import:

import { _try } from 'try-to-result';

Result.collect

Collects multiple results into a single result of an array of values. If all results are successful, it returns Result.ok([values]).

const result = Result.collect([Result.ok(1), Result.ok(2), Result.ok(3)]);
// result = Result.ok([1, 2, 3])

If any result is an error, it returns the first error encountered:

const result = Result.collect([Result.ok(1), Result.ok(2), Result.error('Error')]);
// result = Result.error('Error')

Result.do

Simplifies working with nested results using generator functions:

  • For synchronous generators:

    const result = Result.do(function* (_) { 
      const value = yield* _(Result.ok(42));
    });

    Returns a Result.

    yield* _(result) unpacks a Result.

  • For asynchronous generators:

    const result = await Result.do(async function* (_) { 
      const value = yield* _(await Promise.resolve(Result.ok(42)));
    });

    Returns a Promise<Result>.

    yield* _(await Promise<someResult>) unpacks a Result.

If someResult is Result.error, control flow stops and returns that error.