do-try-tuple
Version:
Catches errors and rejected promises, returns tuple
240 lines (171 loc) • 6.59 kB
Markdown
# do-try-tuple [](https://coveralls.io/github/DScheglov/do-try-tuple?branch=main) [](https://www.npmjs.com/package/do-try-tuple) [](https://www.npmjs.com/package/do-try-tuple) [](https://github.com/DScheglov/do-try-tuple/blob/master/LICENSE)
Catches errors and rejected promises, returns tuple with error and value.
- [Installation](#installation)
- [Usage](#usage)
- [Async Usage](#async-usage)
- [API](#api)
- [`doTry` function](#dotry-function)
- [`safe` promise wrapper](#safe-promise-wrapper)
- [`Failure` type](#failure-type)
- [`Success` type](#success-type)
- [`ErrValueTuple` type](#errvaluetuple-type)
- [`failure` and `success` factory functions](#failure-and-success-factory-functions)
- [`isFailure` and `isSuccess` type guards](#isfailure-and-issuccess-type-guards)
- [Using `doTry().then()`](#using-dotrythen)
## Installation
```bash
npm install do-try-tuple
```
## Usage
```typescript
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
```typescript
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
```typescript
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.
```typescript
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`:
```typescript
function safe<T>(promise: Promise<T>): Promise<ErrValueTuple<T>>;
```
It could be useful when you need to handle the promise rejection synchronously:
```typescript
import { safe } from 'do-try-tuple';
const [areUsersFatched, error, users] = await safe(fetchUsers());
```
### `Failure` type
is a tuple representing the error case:
```typescript
export type Failure<E = unknown> = readonly [ok: false, error: E, value: undefined];
```
The library respects the same motivation as caused introduction
[useUnknownInCatchVariables](https://www.typescriptlang.org/tsconfig/#useUnknownInCatchVariables)
compiler option in TypeScript:
### `Success` type
is a tuple representing the success case:
```typescript
export type Success<T> = readonly [ok: true, error: undefined, value: T];
```
### `ErrValueTuple` type
is a union of `Failure<E>` and `Success<T>`.
```typescript
export type ErrValueTuple<T, E = unknown> = Failure<E> | Success<T>;
```
### `failure` and `success` factory functions
These functions allow to create `ErrValueTuple` instances:
```typescript
export function failure<E>(error: E): Failure<E>;
export function success<T>(value: T): Success<T>;
```
It could be useful in tests:
```typescript
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`:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
const [error, users] = await doTry(async () => fetchUsers()).then(
([err, users]) => [err && new SomeCustomError(err), users] as const,
);
```
So, use
```typescript
// CORRECT _____
const [err, value] = await doTry(async () => someFn(...))
.then(([err, value]) => {
// handle err and value
});
```
instead of
```typescript
// 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.