do-try-tuple
TypeScript icon, indicating that this package has built-in type declarations

3.0.0 • Public • Published

do-try-tuple Coverage Status npm version npm downloads GitHub license

Catches errors and rejected promises, returns tuple with error and value.

Installation

npm install do-try-tuple

Usage

import doTry from 'do-try-tuple';

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

const [isDivOk, errX, x] = doTry(() => div(4, 2));

if (isDivOk) {
  const doubleX = x * 2;
  console.log('doubleX:', doubleX);
}

Async Usage

import doTry from 'do-try-tuple';

const [areUsersFetched, error, users] = await doTry(() => fetchUsers());

if (!areUsersFetched) {
  console.error('Failed to fetch users:', error);
} else {
  console.log('Users:', users);
}

or

import { safe } from 'do-try-tuple';

const [areUsersFetched, error, users] = await safe(fetchUsers());

if (!areUsersFetched) {
  console.error('Failed to fetch users:', error);
} else {
  console.log('Users:', users);
}

API

The library exports:

  • doTry function (default export)
  • safe promise wrapper to make it resolving to ErrValueTuple
  • Failure, Success and ErrValueTuple types
  • failure and success factory functions
  • isFailure and isSuccess type guards

doTry function

takes a function that may throw an error or return a promise that may be rejected.

function (fn: () => never): readonly [false, unknown, never];
function (fn: () => Promise<never>): Promise<readonly [false, unknown, never]>;
function <T>(fn: () => Promise<T>): Promise<ErrValueTuple<T>>;
function <T>(fn: () => T): ErrValueTuple<T>;

safe promise wrapper

is a function that wraps a promise and makes it resolving to ErrValueTuple:

function safe<T>(promise: Promise<T>): Promise<ErrValueTuple<T>>;

It could be useful when you need to handle the promise rejection synchronously:

import { safe } from 'do-try-tuple';

const [areUsersFatched, error, users] = await safe(fetchUsers());

Failure type

is a tuple representing the error case:

export type Failure<E = unknown> = readonly [ok: false, error: E, value: undefined];

The library respects the same motivation as caused introduction useUnknownInCatchVariables compiler option in TypeScript:

Success type

is a tuple representing the success case:

export type Success<T> = readonly [ok: true, error: undefined, value: T];

ErrValueTuple type

is a union of Failure<E> and Success<T>.

export type ErrValueTuple<T, E = unknown> = Failure<E> | Success<T>;

failure and success factory functions

These functions allow to create ErrValueTuple instances:

export function failure<E>(error: E): Failure<E>;
export function success<T>(value: T): Success<T>;

It could be useful in tests:

import { success, failure } from 'do-try-tuple';

test('div', () => {
  expect(doTry(() => div(4, 2))).toEqual(success(2));
  expect(doTry(() => div(4, 0))).toEqual(failure(new Error('Division by Zero')));
  expect(doTry(() => div(0, 0))).toEqual(failure(new Error('Indeterminate Form')));
});

isFailure and isSuccess type guards

These functions allow to check if the value is Failure or Success:

export function isFailure(value: ErrValueTuple<unknown>): value is Failure;
export function isSuccess(value: ErrValueTuple<unknown>): value is Success<unknown>;

It allows to check the result and narrow the type without destructuring:

class DivError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'DivError';
  }
}

function divWithTypeError(a: number, b: number): ErrValueTuple<number, DivError> {
  const result = doTry(() => div(a, b));

  if (isSuccess(result)) return result;
  return failure(new DivError('Failed to divide'));
}

Using doTry().then()

You can map the result of doTry applied to function returning a promise using then method:

import doTry from 'do-try-tuple';

const [error, users] = await doTry(() => fetchUsers()).then(
  ([err, users]) => [err && new SomeCustomError(err), users] as const,
);

However, consider that functions returning promises can throw error synchronously:

const fetchUsers = (): Promise<string[]> => {
  if (Math.random() < 0.5) throw new Error('Failed to fetch users');
  return Promise.resolve(['Alice', 'Bob', 'Charlie']);
};

So, the doTry in this case returns an ErrValueTuple synchronously, and the attempt to call then method on it will throw an error: TypeError: doTry(...).then is not a function.

To handle this case, just add async keyword before fn argument:

const [error, users] = await doTry(async () => fetchUsers()).then(
  ([err, users]) => [err && new SomeCustomError(err), users] as const,
);

So, use

// CORRECT                       _____
const [err, value] = await doTry(async () => someFn(...))
  .then(([err, value]) => {
    // handle err and value
  });

instead of

// WRONG                         ___________
const [err, value] = await doTry(/* async */() => someFn(...))
  .then(([err, value]) => {
    // handle err and value
  });

The same is relevant for any other method of Promise class, like catch, finally, etc.

Package Sidebar

Install

npm i do-try-tuple

Weekly Downloads

387

Version

3.0.0

License

MIT

Unpacked Size

21.3 kB

Total Files

19

Last publish

Collaborators

  • dmitry.scheglov