Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dscheglov/resultage
Provides the Result<T, E> type and tools for handling success and failure, focusing on type safety, great developer experience, and an easy learning curve.
https://github.com/dscheglov/resultage
either err error error-as-values error-handling functor monad never-throw ok result throw
Last synced: 16 days ago
JSON representation
Provides the Result<T, E> type and tools for handling success and failure, focusing on type safety, great developer experience, and an easy learning curve.
- Host: GitHub
- URL: https://github.com/dscheglov/resultage
- Owner: DScheglov
- License: mit
- Created: 2023-12-22T12:52:10.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2024-09-30T13:35:51.000Z (about 1 month ago)
- Last Synced: 2024-09-30T13:40:21.059Z (about 1 month ago)
- Topics: either, err, error, error-as-values, error-handling, functor, monad, never-throw, ok, result, throw
- Language: TypeScript
- Homepage:
- Size: 154 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# resultage [![Coverage Status](https://coveralls.io/repos/github/DScheglov/resultage/badge.svg?branch=main)](https://coveralls.io/github/DScheglov/resultage?branch=main) [![npm version](https://img.shields.io/npm/v/resultage.svg?style=flat-square)](https://www.npmjs.com/package/resultage) [![npm downloads](https://img.shields.io/npm/dm/resultage.svg?style=flat-square)](https://www.npmjs.com/package/resultage) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/DScheglov/resultage/blob/master/LICENSE)
A clear way for handling success and failure in both synchronous and asynchronous operations.
## Installation
```bash
npm install resultage
```## Usage
### Creating a Result
```typescript
import { Result, ok, err } from 'resultage';type JsonObject = Record;
const okIfObject = (value: unknown): Result =>
typeof value === 'object' && value !== null && !Array.isArray(value)
? ok(value as JsonObject)
: err('ERR_NOT_AN_OBJECT');const okIfInt = (value: unknown): Result =>
Number.isInteger(value)
? ok(value as number)
: err('ERR_NOT_AN_INT');const okIfString = (value: unknown): Result =>
typeof value === 'string'
? ok(value)
: err('ERR_NOT_A_STRING');
```### Composing with Do-notation
```typescript
type Person = {
name: string;
age: number;
}const okIfPerson = (value: unknown): Result =>
Do(function*() {
const obj = yield* okIfObject(value);
const name = yield* okIfString(obj.name);
const age = yield* okIfInt(obj.age);return { name, age };
}).mapErr(() => 'ERR_NOT_A_PERSON');const person: Person = okIfPerson({ name: 'John', age: 42 }).unwrap();
```### Composing with chain
```typescript
const okIfPerson =
(value: unknown) => okIfObject(value).chain(
(obj) => okIfString(obj.name).chain(
(name) => okIfInt(obj.age).chain(
(age) => ok({ name, age })
)));
```or the same with `map` on the last step:
```typescript
const okIfPerson =
(value: unknown) => okIfObject(value).chain(
(obj) => okIfString(obj.name).chain(
(name) => okIfInt(obj.age).map(
(age) => ({ name, age })
)));
```> Note: from the performance perspective, using `chain` is preferable to `Do`-notation,
> because `chain` doesn't create and run generators.
> However, `Do`-notation is more readable and easier to use.
> Additionally, the formatting of the code in this section requires specific
> linters and formatters configuration.### Collecting Ok-s from a Result Array
```typescript
const lordOfTheRingsAuthors = collect([
ok({ id, name: 'J. R. R. Tolkien' }),
ok({ id, name: 'Christopher Tolkien' }),
]);const silmarillionAuthors = collect([
ok({ id, name: 'J. R. R. Tolkien' }),
err('ERR_PERSON_NOT_FOUND' as const),
]);console.log(lordOfTheRingsAuthors.unwrap());
// Prints to console:
// [
// { id, name: 'J. R. R. Tolkien' },
// { id, name: 'Christopher Tolkien' }
// ]console.log(silmarillionAuthors.unwrapErr());
// Prints to console: ERR_PERSON_NOT_FOUND
```### Working with Async Results
```typescript
import { Do, collect, err, ok } from 'resultage';const getBookWithAuthors = (bookId: string) =>
Do(async function* () {
const book = yield* await fetchBook(bookId);
const authors = yield* await fetchPersons(book.authorIds);return { ...book, authors };
});const fetchBook = async (id: string) => (
id === '1' ? ok({ id, title: 'The Lord of the Rings', authorIds: ['1', '2'] }) :
id === '2' ? ok({ id, title: 'The Silmarillion', authorIds: ['1', '3'] }) :
err('ERR_BOOK_NOT_FOUND' as const)
);const fetchPersons = async (ids: string[]) => collect(
ids.map(id => (
id === '1' ? ok({ id, name: 'J. R. R. Tolkien' }) :
id === '2' ? ok({ id, name: 'Christopher Tolkien' }) :
err("ERR_PERSON_NOT_FOUND" as const)
))
);async function run() {
const LordOfTheRings = await getBookWithAuthors('1');
console.log(LordOfTheRings.unwrap());
// Prints to console book with authors populatedconst Silmarillion = await getBookWithAuthors('2');
console.log(Silmarillion.unwrapErr());
// Prints to console: ERR_PERSON_NOT_FOUNDconst TheHobbit = await getBookWithAuthors('3');
console.log(TheHobbit.unwrapErr());
// Prints to console: ERR_BOOK_NOT_FOUND
}run().catch(console.error);
```## Documentation
[TODO: insert link to documentation]
## Result Type
`Result` is a generic type that represents either success or failure, and
is an union of `Ok` and `Err` types:
```typescript
type Result = Ok | Err;
```Where:
- `Ok` is a type that represents success and wraps the value of type `T`.
- `Err` is a type that represents failure and wraps the error of type `E`.### `Ok` Interface
`Ok` is an interface that extends the `ResultInterface` interface
with the following structure.```typescript
interface Ok extends ResultInterface {
readonly value: T;
readonly isOk: true;
readonly isErr: false;
}
```The property `value` is accessible only when the type of the correspondent variable
or parameter is narrowed from the `Result` to the `Ok`.To narrow the type of the variable or parameter to `Ok`, use either the `isOk` method
or the `isErr` method on the `Result` instance.**Note**: The `Ok` is an interface, not a class, so it is not possible to create
an instance of `Ok` directly. Use the `ok` function to create an instance of `Ok`.### `Err` Interface
`Err` is an interface that extends the `ResultInterface` interface
with the following structure.```typescript
interface Err extends ResultInterface {
readonly error: E;
readonly isOk: false;
readonly isErr: true;
}
```The property `error` is accessible only when the type of the correspondent variable
or parameter is narrowed from the `Result` to the `Err`.To narrow the type of the variable or parameter to `Err`, use either the `isOk` method
or the `isErr` method on the `Result` instance.**Note**: The `Err` is an interface, not a class, so it is not possible to create
an instance of `Err` directly. Use the `err` function to create an instance of `Err`.### `ResultInterface` Interface
`ResultInterface` is an interface that defines the common `Result` methods.
```typescript
interface ResultInterface {
map(fn: (data: T) => S): Result;
mapErr(fn: (error: E) => F): Result;
chain(next: (data: T) => Result): Result;
chainErr(next: (error: E) => Result): Result;
unwrap(): T;
unwrapOr(fallback: S): T | S;
unwrapOrElse(fallback: (error: E) => S): T | S;
unwrapErr(): E;
unwrapErrOr(fallback: F): E | F;
unwrapErrOrElse(fallback: (data: T) => F): E | F;
unwrapOrThrow(): T;
unpack(): T | E;
match(
okMatcher: (data: T) => TR,
errMatcher: (error: E) => ER,
): ER | TR;
tap(fn: (data: T) => void): Result;
tapErr(fn: (error: E) => void): Result;
biMap(okFn: (data: T) => S, errFn: (error: E) => F): Result;
biChain(
okFn: (data: T) => Result,
errFn: (error: E) => Result,
): Result;
[Symbol.iterator](): Generator;
}
```## Constructors
As mentioned above, `Ok` and `Err` are interfaces, not classes, so it is not
possible to create an instance of `Ok` or `Err` directly. Use the following
functions to create an instance of `Ok` or `Err`.### Function ok(value)
Creates an instance of `OkImpl` class (that is not exported from the package).
Function Signature:
```typescript
const ok: (value: T) => Ok
```Example:
```typescript
import { ok } from 'resultage';const okNumber = ok(42);
```### Function err(error)
Creates an instance of `ErrImpl` class (that is not exported from the package).
Function Signature:
```typescript
const err: (error: E) => Err
```Example:
```typescript
import { err } from 'resultage';const errString = err('Error message');
```## Properties And Methods of `Result`
### Property .isOk: boolean
Returns `true` if Result is `Ok`, `false` otherwise. Narrows the `Result` to `Ok` in "if"-branches,
and to `Err` in "else"-branches.Property Definition:
```typescript
interface Ok { readonly isOk: true }
interface Err { readonly isOk: false }
```Function Signature:
```typescript
const isOk: (result: Result) => result is Ok
```Example:
```typescript
import { ok } from 'resultage';const result = ok(42);
if (result.isOk) {
console.log(result.value);
} else {
console.error(result.error);
}
```Example with function:
```typescript
import { ok, isOk } from 'resultage';const result = ok(42);
if (isOk(result)) {
console.log(result.value);
} else {
console.error(result.error);
}
```The function `isOk(result)` is good to be used as a callback in
the `Array.prototype.filter` method or similar.```typescript
import { isOk } from 'resultage';const results = [ok(42), err('Error')];
const isEverythingOk = results.every(isOk);
```### Property .isErr: boolean
Returns `true` if Result is `Err`, `false` otherwise. Narrows the `Result` to `Err` in "if"-branches,
and to `Ok` in "else"-branches.Property Definition:
```typescript
interface Ok { readonly isErr: false }
interface Err { readonly isErr: true }
```Function Signature:
```typescript
const isErr: (result: Result): result is Err
```Example:
```typescript
import { err } from 'resultage';const result = err('Error message');
if (result.isErr) {
console.error(result.error);
} else {
console.log(result.value);
}
```Example with function:
```typescript
import { err, isErr } from 'resultage';const result = err('Error message');
if (isErr(result)) {
console.error(result.error);
} else {
console.log(result.value);
}
```The function `isErr(result)` is good to be used as a callback in
the `Array.prototype.filter` method or similar.```typescript
import { isErr } from 'resultage';const results = [ok(42), err('Error')];
const isSomethingWrong = results.some(isErr);
```### Ok Property .value: T
Returns the value of `Ok`. Could be accessed if and only if the `Result`
is explicitly narrowed to `Ok`.Property Definition:
```typescript
interface Ok { readonly value: T }
```Example:
```typescript
import { ok } from 'resultage';const result = ok(42);
console.log(result.value); // Prints to console: 42
```Example with narrowing:
```typescript
import { ok, err } from 'resultage';const okIfOdd = (value: number) =>
value % 2 === 1
? ok(value)
: err('Value is not odd');const result = okIfOdd(43);
result.value;
// ^^^^^ - Error: Property 'value' does not exist on type 'Result'.if (result.isOk) {
console.log(result.value);
} else {
console.error(result.error);
}
```### Err Property .error: E
Returns the error of `Err`. Could be accessed if and only if the `Result`
is explicitly narrowed to `Err`.Property Definition:
```typescript
interface Err { readonly error: E }
```Example:
```typescript
import { err } from 'resultage';const result = err('Error message');
console.log(result.error); // Prints to console: Error message
```Example with narrowing:
```typescript
import { ok, err } from 'resultage';const okIfOdd = (value: number) =>
value % 2 === 1
? ok(value)
: err('Value is not odd');const result = okIfOdd(42);
result.error;
// ^^^^^ - Error: Property 'error' does not exist on type 'Result'.if (result.isErr) {
console.error(result.error);
} else {
console.log(result.value);
}
```### Method .map(fn)
Applies `fn` to the value of `Ok` and returns the value wrapped in `Ok`. If `Result` is `Err` returns itself without applying `fn`.
Method Signature:
```typescript
interface ResultInterface {
map(fn: (data: T) => S): Result
}
```Curried Function Signature:
```typescript
const map:
(fn: (data: T) => S) =>
(result: Result) => Result
```Example:
```typescript
import { ok } from 'resultage';const result = ok(42);
const mappedResult = result.map(value => value * 2);
console.log(mappedResult.value); // Prints to console: 84
```### Method .mapErr(fn)
Applies `fn` to the value of `Err` and returns the value wrapped in `Err`. If `Result` is `Ok` returns itself without applying `fn`.
Method Signature:
```typescript
interface ResultInterface {
mapErr(fn: (error: E) => F): Result
}
```Curried Function Signature:
```typescript
const mapErr:
(fn: (error: E) => F) =>
(result: Result) => Result
```Example:
```typescript
import { err } from 'resultage';const result = err('Error message');
const mappedResult = result.mapErr(error => new Error(error));
```### Method .chain(next)
Applies `next` to the value of `Ok` and returns the result of `next`. If the `Result` is `Err`,
returns itself without applying `next`.The next function must return a `Result`.
Method Signature:
```typescript
interface ResultInterface {
chain(next: (data: T) => Result): Result
}
```Curried Function Signature:
```typescript
const chain:
(next: (data: T) => Result) =>
(result: Result) => Result
```Example:
```typescript
import { ok } from 'resultage';const result = ok(42);
const chainedResult = result.chain(value => ok(value * 2));
```The `chain` method is a main method to compose `(...) => Result` functions.
### Method .chainErr(next)
Applies `next` to the value of `Err` and returns the result of `next`.
If the `Result` is `Ok`, returns itself without applying `next`.The next function must return a `Result`.
Method Signature:
```typescript
interface ResultInterface {
chainErr(next: (error: E) => Result): Result
}
```Curried Function Signature:
```typescript
const chainErr:
(next: (error: E) => Result) =>
(result: Result) => Result
```Example:
```typescript
import { err } from 'resultage';const result = err('Error message');
const chainedResult = result.chainErr(error => err(new Error(error)));
```The `chainErr` is a convenient method to recover from an error.
```typescript
import { err, ok } from 'resultage';const okIfOdd = (value: number) =>
value % 2 === 1
? ok(value)
: err('Value is not odd');const getOdd = (value: number): number =>
okIfOdd(value)
.chainErr(() => ok(value + 1))
.unwrap();console.log(getOdd(1)); // 1
```### Method .unwrap()
Returns the value of `Ok`. If the `Result` is `Err` throws a `TypeError`
where `cause` is the result.Method Signature:
```typescript
interface ResultInterface {
unwrap(): T
}
```Function Signature:
```typescript
const unwrap: (result: Result) => T
```Example:
```typescript
import { ok } from 'resultage';const result = ok(42);
console.log(result.unwrap()); // Prints to console: 42
```Example with error:
```typescript
import { err } from 'resultage';const result = err('Error message');
console.log(result.unwrap());
// Throws a TypeError with the message: 'Result is not an Ok' and cause equal
// to the result.
```### Method .unwrapOr(fallback)
Returns the value of `Ok`. If the `Result` is `Err` returns `fallback`.
Method Signature:
```typescript
interface ResultInterface {
unwrapOr(fallback: S): T | S
}
```Curried Function Signature:
```typescript
const unwrapOr:
(fallback: S) =>
(result: Result) => T | S
```### Method .unwrapOrThrow()
Returns the value of `Ok`. If the `Result` is `Err` throws a value of
type `E`.`unwrapOrThrow` doesn't check if `E` is an instance of `Error` or not, so it is
possible to throw a non-error literal.Method Signature:
```typescript
interface ResultInterface {
unwrapOrThrow(): T
}
```Function Signature:
```typescript
const unwrapOrThrow: (result: Result) => T
```### Method .unwrapOrElse
Returns the value of `Ok`. If the `Result` is `Err` returns the result of `fallbackFn`.
Method Signature:
```typescript
interface ResultInterface {
unwrapOrElse(fallbackFn: (error: E) => S): T | S
}
```Curried Function Signature:
```typescript
const unwrapOrElse:
(fallbackFn: (error: unknown) => S) =>
(result: Result) => T | S
```### Method .unwrapErr
Returns the value of `Err`. If the `Result` is `Ok` throws a `TypeError` where `cause` is the `Ok`.
Method Signature:
```typescript
interface ResultInterface {
unwrapErr(): E
}
```Function Signature:
```typescript
const unwrapErr: (result: Result) => E
```### Method .unwrapErrOr(fallback)
Returns the value of `Err`. If the `Result` is `Ok` returns `fallback`.
Method Signature:
```typescript
interface ResultInterface {
unwrapErrOr(fallback: F): E | F
}
```Curried Function Signature:
```typescript
const unwrapErrOr:
(fallback: F) =>
(result: Result) => E | F
```### Method .unwrapErrOrElse(fallbackFn)
Returns the value of `Err`. If the `Result` is `Ok` returns the result of `fallback`.
Method Signature:
```typescript
interface ResultInterface {
unwrapErrOrElse(fallbackFn: (data: T) => F): E | F
}
```Curried Function Signature:
```typescript
const unwrapErrOrElse:
(fallbackFn: (data: T) => F) =>
(result: Result) => E | F
```### Method .unpack()
Returns the value of `Ok` or `Err`.
Method Signature:
```typescript
interface ResultInterface {
unpack(): T | E
}
```Function Signature:
```typescript
const unpack: (result: Result) => T | E
```### Method .match(okMatcher, errMatcher)
Applies `okMatcher` to the value of `Ok` and returns the result. Applies `errMatcher` to the value of `Err` and returns the result.
Method Signature:
```typescript
interface ResultInterface {
match(okMatcher: (data: T) => S, errMatcher: (error: E) => F): S | F
}
```Curried Function Signature:
```typescript
const match:
(okMatcher: (data: T) => S, errMatcher: (error: E) => F) =>
(result: Result) => S | F
```### Method .tap(fn)
Applies `fn` to the value of `Ok` and returns the original result. If the `Result` is `Err` doesn't apply `fn`.
Method Signature:
```typescript
interface ResultInterface {
tap(fn: (data: T) => void): Result
}
```Curried Function Signature:
```typescript
const tap:
(fn: (data: T) => void) =>
(result: Result) => Result
```### Method .tapErr(fn)
Applies `fn` to the value of `Err` and returns the original result. If the `Result` is `Ok` doesn't apply `fn`.
Method Signature:
```typescript
interface ResultInterface {
tapErr(fn: (error: E) => void): Result
}
```Curried Function Signature:
```typescript
const tapErr:
(fn: (error: E) => void) =>
(result: Result) => Result
```## Operating on Multiple Results
### collect(results)
Collects `Ok` values from an array of `Result` and returns a `Result`.
Function Signature:
```typescript
const collect:
[]>(results: R) => Result, ErrTypeOf>
```