neverthrow
Advanced tools
Comparing version 2.8.0-beta.1 to 3.0.0
@@ -37,2 +37,8 @@ 'use strict'; | ||
}; | ||
// need to figure how to overload to prevent nesting of values | ||
// https://github.com/supermacro/neverthrow/issues/183#issuecomment-730554613 | ||
Ok.prototype.andThenCollect = function (f) { | ||
var _this = this; | ||
return f(this.value).map(function (newVal) { return [newVal, _this.value]; }); | ||
}; | ||
Ok.prototype.asyncAndThen = function (f) { | ||
@@ -243,131 +249,2 @@ return f(this.value); | ||
var toPromise = function (val) { | ||
return val instanceof Promise ? val : Promise.resolve(val); | ||
}; | ||
// Functor implementation for functions | ||
// but tailored specifically for Result | ||
// https://eli.thegreenplace.net/2018/haskell-functions-as-functors-applicatives-and-monads/ | ||
var mapFn = function (g, ff) { | ||
return function (val) { | ||
return g(ff(val)); | ||
}; | ||
}; | ||
var chain = function (r1, r2) { return __awaiter(void 0, void 0, void 0, function () { | ||
var warning, inner, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
warning = [ | ||
'DEPRECATION WARNING', | ||
'The `chain` API will likely be deprecated in a subsequent version of neverthrow', | ||
'Please use the `ResultAsync` API: https://github.com/supermacro/neverthrow/#asynchronous-api', | ||
].join(' - '); | ||
logWarning(warning); | ||
return [4 /*yield*/, r1]; | ||
case 1: | ||
inner = _a.sent(); | ||
return [4 /*yield*/, inner.asyncMap(r2)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain3 = function (r1, r2, r3) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r2Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r2Promise = mapFn(toPromise, r2); | ||
return [4 /*yield*/, chain(r1, r2Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r3)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain4 = function (r1, r2, r3, r4) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r3Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r3Promise = mapFn(toPromise, r3); | ||
return [4 /*yield*/, chain3(r1, r2, r3Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r4)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain5 = function (r1, r2, r3, r4, r5) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r4Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r4Promise = mapFn(toPromise, r4); | ||
return [4 /*yield*/, chain4(r1, r2, r3, r4Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r5)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain6 = function (r1, r2, r3, r4, r5, r6) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r5Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r5Promise = mapFn(toPromise, r5); | ||
return [4 /*yield*/, chain5(r1, r2, r3, r4, r5Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r6)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain7 = function (r1, r2, r3, r4, r5, r6, r7) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r6Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r6Promise = mapFn(toPromise, r6); | ||
return [4 /*yield*/, chain6(r1, r2, r3, r4, r5, r6Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r7)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain8 = function (r1, r2, r3, r4, r5, r6, r7, r8) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r7Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r7Promise = mapFn(toPromise, r7); | ||
return [4 /*yield*/, chain7(r1, r2, r3, r4, r5, r6, r7Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r8)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
/** | ||
@@ -406,9 +283,2 @@ * Short circuits on the FIRST Err value that we find | ||
exports.ResultAsync = ResultAsync; | ||
exports.chain = chain; | ||
exports.chain3 = chain3; | ||
exports.chain4 = chain4; | ||
exports.chain5 = chain5; | ||
exports.chain6 = chain6; | ||
exports.chain7 = chain7; | ||
exports.chain8 = chain8; | ||
exports.combine = combine; | ||
@@ -415,0 +285,0 @@ exports.err = err; |
@@ -12,2 +12,3 @@ declare type Result<T, E> = Ok<T, E> | Err<T, E>; | ||
andThen<U>(f: (t: T) => Result<U, E>): Result<U, E>; | ||
andThenCollect<U>(f: (currentVal: T) => Result<U, E>): Result<[U, T], E>; | ||
asyncAndThen<U>(f: (t: T) => ResultAsync<U, E>): ResultAsync<U, E>; | ||
@@ -50,13 +51,5 @@ asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E>; | ||
declare const chain: <T1, T2, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Promise<Result<T2, E>>) => Promise<Result<T2, E>>; | ||
declare const chain3: <T1, T2, T3, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Promise<Result<T3, E>>) => Promise<Result<T3, E>>; | ||
declare const chain4: <T1, T2, T3, T4, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Promise<Result<T4, E>>) => Promise<Result<T4, E>>; | ||
declare const chain5: <T1, T2, T3, T4, T5, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Promise<Result<T5, E>>) => Promise<Result<T5, E>>; | ||
declare const chain6: <T1, T2, T3, T4, T5, T6, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Promise<Result<T6, E>>) => Promise<Result<T6, E>>; | ||
declare const chain7: <T1, T2, T3, T4, T5, T6, T7, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Ok<T6, E> | Err<T6, E> | Promise<Result<T6, E>>, r7: (v: T6) => Promise<Result<T7, E>>) => Promise<Result<T7, E>>; | ||
declare const chain8: <T1, T2, T3, T4, T5, T6, T7, T8, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Ok<T6, E> | Err<T6, E> | Promise<Result<T6, E>>, r7: (v: T6) => Ok<T7, E> | Err<T7, E> | Promise<Result<T7, E>>, r8: (v: T7) => Promise<Result<T8, E>>) => Promise<Result<T8, E>>; | ||
declare function combine<T, E>(resultList: Result<T, E>[]): Result<T[], E>; | ||
declare function combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[], E>; | ||
export { Err, Ok, Result, ResultAsync, chain, chain3, chain4, chain5, chain6, chain7, chain8, combine, err, errAsync, ok, okAsync }; | ||
export { Err, Ok, Result, ResultAsync, combine, err, errAsync, ok, okAsync }; |
@@ -33,2 +33,8 @@ // eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
}; | ||
// need to figure how to overload to prevent nesting of values | ||
// https://github.com/supermacro/neverthrow/issues/183#issuecomment-730554613 | ||
Ok.prototype.andThenCollect = function (f) { | ||
var _this = this; | ||
return f(this.value).map(function (newVal) { return [newVal, _this.value]; }); | ||
}; | ||
Ok.prototype.asyncAndThen = function (f) { | ||
@@ -239,131 +245,2 @@ return f(this.value); | ||
var toPromise = function (val) { | ||
return val instanceof Promise ? val : Promise.resolve(val); | ||
}; | ||
// Functor implementation for functions | ||
// but tailored specifically for Result | ||
// https://eli.thegreenplace.net/2018/haskell-functions-as-functors-applicatives-and-monads/ | ||
var mapFn = function (g, ff) { | ||
return function (val) { | ||
return g(ff(val)); | ||
}; | ||
}; | ||
var chain = function (r1, r2) { return __awaiter(void 0, void 0, void 0, function () { | ||
var warning, inner, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
warning = [ | ||
'DEPRECATION WARNING', | ||
'The `chain` API will likely be deprecated in a subsequent version of neverthrow', | ||
'Please use the `ResultAsync` API: https://github.com/supermacro/neverthrow/#asynchronous-api', | ||
].join(' - '); | ||
logWarning(warning); | ||
return [4 /*yield*/, r1]; | ||
case 1: | ||
inner = _a.sent(); | ||
return [4 /*yield*/, inner.asyncMap(r2)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain3 = function (r1, r2, r3) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r2Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r2Promise = mapFn(toPromise, r2); | ||
return [4 /*yield*/, chain(r1, r2Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r3)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain4 = function (r1, r2, r3, r4) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r3Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r3Promise = mapFn(toPromise, r3); | ||
return [4 /*yield*/, chain3(r1, r2, r3Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r4)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain5 = function (r1, r2, r3, r4, r5) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r4Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r4Promise = mapFn(toPromise, r4); | ||
return [4 /*yield*/, chain4(r1, r2, r3, r4Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r5)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain6 = function (r1, r2, r3, r4, r5, r6) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r5Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r5Promise = mapFn(toPromise, r5); | ||
return [4 /*yield*/, chain5(r1, r2, r3, r4, r5Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r6)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain7 = function (r1, r2, r3, r4, r5, r6, r7) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r6Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r6Promise = mapFn(toPromise, r6); | ||
return [4 /*yield*/, chain6(r1, r2, r3, r4, r5, r6Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r7)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
var chain8 = function (r1, r2, r3, r4, r5, r6, r7, r8) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r7Promise, chained, mapped; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
r7Promise = mapFn(toPromise, r7); | ||
return [4 /*yield*/, chain7(r1, r2, r3, r4, r5, r6, r7Promise)]; | ||
case 1: | ||
chained = _a.sent(); | ||
return [4 /*yield*/, chained.asyncMap(r8)]; | ||
case 2: | ||
mapped = _a.sent(); | ||
return [2 /*return*/, mapped.andThen(function (inner) { return inner; })]; | ||
} | ||
}); | ||
}); }; | ||
/** | ||
@@ -399,2 +276,2 @@ * Short circuits on the FIRST Err value that we find | ||
export { Err, Ok, ResultAsync, chain, chain3, chain4, chain5, chain6, chain7, chain8, combine, err, errAsync, ok, okAsync }; | ||
export { Err, Ok, ResultAsync, combine, err, errAsync, ok, okAsync }; |
{ | ||
"name": "neverthrow", | ||
"version": "2.8.0-beta.1", | ||
"version": "3.0.0", | ||
"description": "Stop throwing errors, and instead return Results!", | ||
@@ -27,19 +27,19 @@ "main": "dist/index.cjs.js", | ||
"devDependencies": { | ||
"@babel/core": "7.11.6", | ||
"@babel/preset-env": "7.11.5", | ||
"@babel/preset-typescript": "7.10.4", | ||
"@types/jest": "26.0.14", | ||
"@types/node": "14.11.2", | ||
"@typescript-eslint/eslint-plugin": "4.2.0", | ||
"@typescript-eslint/parser": "4.2.0", | ||
"babel-jest": "26.3.0", | ||
"eslint": "7.9.0", | ||
"eslint-config-prettier": "6.12.0", | ||
"@babel/core": "7.12.3", | ||
"@babel/preset-env": "7.12.1", | ||
"@babel/preset-typescript": "7.12.1", | ||
"@types/jest": "26.0.15", | ||
"@types/node": "14.14.7", | ||
"@typescript-eslint/eslint-plugin": "4.5.0", | ||
"@typescript-eslint/parser": "4.5.0", | ||
"babel-jest": "26.6.3", | ||
"eslint": "7.12.0", | ||
"eslint-config-prettier": "6.14.0", | ||
"eslint-plugin-prettier": "3.1.4", | ||
"jest": "26.4.2", | ||
"jest": "26.6.3", | ||
"prettier": "2.1.2", | ||
"rollup": "2.28.2", | ||
"rollup": "2.32.1", | ||
"rollup-plugin-dts": "1.4.13", | ||
"rollup-plugin-typescript2": "0.27.3", | ||
"ts-jest": "26.4.0", | ||
"rollup-plugin-typescript2": "0.29.0", | ||
"ts-jest": "26.4.2", | ||
"typescript": "4.0.3" | ||
@@ -46,0 +46,0 @@ }, |
313
README.md
@@ -5,4 +5,2 @@ # NeverThrow 🙅 | ||
If you find this package useful, please consider [sponsoring me](https://github.com/sponsors/supermacro/) or simply [buying me a coffee](https://ko-fi.com/gdelgado)! | ||
## Description | ||
@@ -14,13 +12,40 @@ | ||
For asynchronous tasks, `neverthrow` offers a `ResultAsync` class which wraps a `Promise<Result>` and enables chaining. | ||
`ResultAsync` is `thenable` meaning it behaves exactly like a native `Promise<Result>`: the underlying `Result` can be accessed using the `await` or `.then()` operators. | ||
For asynchronous tasks, `neverthrow` offers a `ResultAsync` class which wraps a `Promise<Result<T, E>>` and gives you the same level of expressivity and control as a regular `Result<T, E>`. | ||
[Read the blog post](https://gdelgado.ca/type-safe-error-handling-in-typescript.html#title) which explains _why you'd want to use this package_. | ||
`ResultAsync` is `thenable` meaning it **behaves exactly like a native `Promise<Result>`** ... except you have access to the same methods that `Result` provides without having to `await` or `.then` the promise! Check out [the wiki](https://github.com/supermacro/neverthrow/wiki/Basic-Usage-Examples#asynchronous-api) for examples and best practices. | ||
This package works for both JS and TypeScript. However, the types that this package provides will allow you to get compile-time guarantees around error handling if you are using TypeScript. | ||
> Need to see real-life examples of how to leverage this package for error handling? See this repo: https://github.com/parlez-vous/server | ||
`neverthrow` draws inspiration from [Rust](https://doc.rust-lang.org/std/result/enum.Result.html), and [Elm](https://package.elm-lang.org/packages/elm/core/latest/Result). It is also a great companion to [fp-ts](https://gcanti.github.io/fp-ts/). | ||
<div id="toc"></div> | ||
> Need to see real-life examples of how to leverage this package for error handling? See this repo: https://github.com/parlez-vous/server | ||
## Table Of Contents | ||
* [Installation](#installation) | ||
* [Top-Level API](#top-level-api) | ||
* [API Documentation](#api-documentation) | ||
+ [Synchronous API (`Result`)](#synchronous-api-result) | ||
- [`ok`](#ok) | ||
- [`err`](#err) | ||
- [`Result.isOk` (method)](#resultisok-method) | ||
- [`Result.isErr` (method)](#resultiserr-method) | ||
- [`Result.map` (method)](#resultmap-method) | ||
- [`Result.mapErr` (method)](#resultmaperr-method) | ||
- [`Result.unwrapOr` (method)](#resultunwrapor-method) | ||
- [`Result.andThen` (method)](#resultandthen-method) | ||
- [`Result.asyncAndThen` (method)](#resultasyncandthen-method) | ||
- [`Result.match` (method)](#resultmatch-method) | ||
- [`Result.asyncMap` (method)](#resultasyncmap-method) | ||
+ [Asynchronous API (`ResultAsync`)](#asynchronous-api-resultasync) | ||
- [`okAsync`](#okasync) | ||
- [`errAsync`](#errasync) | ||
- [`ResultAsync.fromPromise` (static class method)](#resultasyncfrompromise-static-class-method) | ||
- [`ResultAsync.map` (method)](#resultasyncmap-method) | ||
- [`ResultAsync.mapErr` (method)](#resultasyncmaperr-method) | ||
- [`ResultAsync.unwrapOr` (method)](#resultasyncunwrapor-method) | ||
- [`ResultAsync.andThen` (method)](#resultasyncandthen-method) | ||
- [`ResultAsync.match` (method)](#resultasyncmatch-method) | ||
+ [Utilities](#utilities) | ||
- [`combine`](#combine) | ||
* [A note on the Package Name](#a-note-on-the-package-name) | ||
## Installation | ||
@@ -32,100 +57,2 @@ | ||
## Usage | ||
### Synchronous API | ||
Create `Ok` or `Err` instances with the `ok` and `err` functions. | ||
```typescript | ||
import { ok, err } from 'neverthrow' | ||
// something awesome happend | ||
const yesss = ok(someAesomeValue) | ||
// moments later ... | ||
const mappedYes = yesss.map(doingSuperUsefulStuff) | ||
// neverthrow uses type-guards to differentiate between Ok and Err instances | ||
// Mode info: https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types | ||
if (mappedYes.isOk()) { | ||
// using type guards, we can access an Ok instance's `value` field | ||
doStuffWith(mappedYes.value) | ||
} else { | ||
// because of type guards | ||
// typescript knows that mappedYes is an Err instance and thus has a `error` field | ||
doStuffWith(mappedYes.error) | ||
} | ||
``` | ||
`Result` is defined as follows: | ||
```typescript | ||
type Result<T, E> = Ok<T, E> | Err<T, E> | ||
``` | ||
`Ok<T, E>`: contains the success value of type `T` | ||
`Err<T, E>`: contains the failure value of type `E` | ||
--- | ||
### Asynchronous API | ||
Asynchronous methods can return a `ResultAsync` type instead of a `Promise<Result>` in order to enable further chaining. | ||
`ResultAsync` is `thenable` meaning it behaves exactly like a native `Promise<Result>`: the underlying `Result` can be accessed using the `await` or `.then()` operators. | ||
This is useful for handling multiple asynchronous apis like database queries, timers, http requests, ... | ||
Example: | ||
```typescript | ||
import { errAsync, ResultAsync } from 'neverthrow' | ||
import { insertIntoDb } from 'imaginary-database' | ||
// Let's assume insertIntoDb has the following signature: | ||
// insertIntoDb(user: User): Promise<User> | ||
// We can create a synchronous method that returns a ResultAsync | ||
function addUserToDatabase(user: User): ResultAsync<User, Error> { | ||
if (user.name.length < 3) { | ||
// Throw a async result from a synchronous block thanks to the errAsync helper | ||
return errAsync(new Error('Username is too short')) | ||
} | ||
// Wrap the async method into a ResultAsync thanks to fromPromise | ||
// The seconds argument catches the error from the promise | ||
return ResultAsync.fromPromise(insertIntoDb(user), () => new Error('Database error')) | ||
} | ||
// We can now call the method above | ||
const asyncRes = addUserToDatabase({ name: 'Tom' }) // asyncRes is a `ResultAsync<User, Error>` | ||
// We can chain the ResultAsync to build another ResultAsync (see full api below) | ||
const asyncRes2 = asyncRes.map((user: User) => user.name) // asyncRes2 is a `ResultAsync<string, Error>` | ||
// A ResultAsync acts exactly like a Promise<Result> | ||
// It can be transformed back into a Result just like a Promise would: | ||
// using await | ||
const res = await asyncRes | ||
// res is a Result<string, Error> | ||
if (res.isErr()) { | ||
console.log('Oops fail: ' + res.error.message) | ||
} else { | ||
console.log('Successfully inserted user ' + res.value) | ||
} | ||
// using then | ||
asyncRes.then(res => { | ||
// res is Result<string, Error> | ||
if (res.isErr()) { | ||
console.log('Oops fail: ' + res.error.message) | ||
} else { | ||
console.log('Successfully inserted user ' + res.value) | ||
} | ||
}) | ||
``` | ||
## Top-Level API | ||
@@ -143,3 +70,5 @@ | ||
- `errAsync` convenience function to create a `ResultAsync` containing an `Err` type `Result` | ||
- `combine` utility function that allows you to turn `Result<T, E>[]` into `Result<T[], E>`, or a `ResultAsync<T, E>[]` into `ResultAsync<T[], E>` (just like `Promise.all`) | ||
```typescript | ||
@@ -154,37 +83,21 @@ import { | ||
errAsync, | ||
ResultAsync | ||
ResultAsync, | ||
combine | ||
} from 'neverthrow' | ||
``` | ||
## Accessing the value inside of a Result | ||
--- | ||
This library takes advantage of TypeScript's [type-guard feature](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types). | ||
**Check out the [wiki](https://github.com/supermacro/neverthrow/wiki) for help on how to make the most of `neverthrow`.** | ||
By simply doing an `if` (using `.isOk` or `.isErr`) check on your result, you can inform the TypeScript compiler of whether you have `Ok` instance, or an `Err` instance, and subsequently you can get access to the `value` or `error` value in the respective instances. | ||
If you find this package useful, please consider [sponsoring me](https://github.com/sponsors/supermacro/) or simply [buying me a coffee](https://ko-fi.com/gdelgado)! | ||
Example: | ||
--- | ||
```typescript | ||
import { ok, err } from 'neverthrow' | ||
## API Documentation | ||
const example1 = ok(123) | ||
const example2 = err('abc') | ||
### Synchronous API (`Result`) | ||
if (example1.isOk()) { | ||
// you now have access to example1.value | ||
} else { | ||
// you now have access to example1.error | ||
} | ||
#### `ok` | ||
if (example2.isErr()) { | ||
// you now have access to example2.error | ||
} else { | ||
// you now have access to example2.value | ||
} | ||
``` | ||
## API | ||
### `ok` | ||
Constructs an `Ok` variant of `Result` | ||
@@ -209,5 +122,7 @@ | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `err` | ||
#### `err` | ||
@@ -233,5 +148,7 @@ Constructs an `Err` variant of `Result` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.isOk` (method) | ||
#### `Result.isOk` (method) | ||
@@ -243,8 +160,10 @@ Returns `true` if the result is an `Ok` variant | ||
```typescript | ||
isOk(): boolean { ... } | ||
isOk(): boolean { ... } | ||
``` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.isErr` (method) | ||
#### `Result.isErr` (method) | ||
@@ -256,8 +175,10 @@ Returns `true` if the result is an `Err` variant | ||
```typescript | ||
isErr(): boolean { ... } | ||
isErr(): boolean { ... } | ||
``` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.map` (method) | ||
#### `Result.map` (method) | ||
@@ -271,3 +192,3 @@ Maps a `Result<T, E>` to `Result<U, E>` by applying a function to a contained `Ok` value, leaving an `Err` value untouched. | ||
```typescript | ||
type MapFunc = <T>(f: T) => U | ||
type MapFunc = <T, U>(f: T) => U | ||
map<U>(fn: MapFunc): Result<U, E> { ... } | ||
@@ -296,5 +217,7 @@ ``` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.mapErr` (method) | ||
#### `Result.mapErr` (method) | ||
@@ -332,5 +255,7 @@ Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.unwrapOr` (method) | ||
#### `Result.unwrapOr` (method) | ||
@@ -342,3 +267,3 @@ Unwrap the `Ok` value, or return the default if there is an `Err` | ||
```typescript | ||
unwrapOr<T>(v: T): T { ... } | ||
unwrapOr<T>(v: T): T { ... } | ||
``` | ||
@@ -356,5 +281,7 @@ | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.andThen` (method) | ||
#### `Result.andThen` (method) | ||
@@ -373,3 +300,3 @@ Same idea as `map` above. Except you must return a new `Result`. | ||
type AndThenFunc = (t: T) => Result<U, E> | ||
type AndThenFunc = <T, U>(t: T) => Result<U, E> | ||
andThen<U>(f: AndThenFunc): Result<U, E> { ... } | ||
@@ -413,5 +340,7 @@ | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.asyncAndThen` (method) | ||
#### `Result.asyncAndThen` (method) | ||
@@ -431,5 +360,7 @@ Same idea as `andThen` above. Except you must return a new `ResultAsync`. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.match` (method) | ||
#### `Result.match` (method) | ||
@@ -444,4 +375,4 @@ Given 2 functions (one for the `Ok` variant and one for the `Err` variant) execute the function that matches the `Result` variant. | ||
match<A>( | ||
okFn: (t: T) => A, | ||
errFn: (e: E) => A | ||
okFn: (t: T) => A, | ||
errFn: (e: E) => A | ||
): A => { ... } | ||
@@ -477,5 +408,7 @@ ``` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `Result.asyncMap` (method) | ||
#### `Result.asyncMap` (method) | ||
@@ -492,3 +425,3 @@ Similar to `map` except for two things: | ||
```typescript | ||
type MappingFunc = (t: T) => Promise<U> | ||
type MappingFunc = (t: T) => Promise<U> | ||
asyncMap<U>(fn: MappingFunc): ResultAsync<U, E> { ... } | ||
@@ -510,7 +443,11 @@ ``` | ||
Note that in the above example if `parseHeaders` returns an `Err` then `.map` and `.asyncMap` will not be invoked, and `asyncRes` variable will resolve to an `Err` when turned into a `Result` using `await` or `.then()`. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `okAsync` | ||
### Asynchronous API (`ResultAsync`) | ||
#### `okAsync` | ||
Constructs an `Ok` variant of `ResultAsync` | ||
@@ -521,3 +458,3 @@ | ||
```typescript | ||
okAsync<T, E>(value: T): ResultAsync<T, E> | ||
okAsync<T, E>(value: T): ResultAsync<T, E> | ||
``` | ||
@@ -537,6 +474,8 @@ | ||
``` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `errAsync` | ||
#### `errAsync` | ||
@@ -564,5 +503,7 @@ Constructs an `Err` variant of `ResultAsync` | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.fromPromise` (method) | ||
#### `ResultAsync.fromPromise` (static class method) | ||
@@ -582,2 +523,3 @@ Transforms a `Promise<T>` into a `ResultAsync<T, E>`. | ||
```typescript | ||
import { ResultAsync } from 'neverthrow' | ||
import { insertIntoDb } from 'imaginary-database' | ||
@@ -590,5 +532,7 @@ // insertIntoDb(user: User): Promise<User> | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.map` (method) | ||
#### `ResultAsync.map` (method) | ||
@@ -632,5 +576,7 @@ Maps a `ResultAsync<T, E>` to `ResultAsync<U, E>` by applying a function to a contained `Ok` value, leaving an `Err` value untouched. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.mapErr` (method) | ||
#### `ResultAsync.mapErr` (method) | ||
@@ -683,5 +629,7 @@ Maps a `ResultAsync<T, E>` to `ResultAsync<T, F>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.unwrapOr` (method) | ||
#### `ResultAsync.unwrapOr` (method) | ||
@@ -704,5 +652,7 @@ Unwrap the `Ok` value, or return the default if there is an `Err`. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.andThen` (method) | ||
#### `ResultAsync.andThen` (method) | ||
@@ -753,5 +703,7 @@ Same idea as `map` above. Except the applied function must return a `Result` or `ResultAsync`. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### `ResultAsync.match` (method) | ||
#### `ResultAsync.match` (method) | ||
@@ -785,4 +737,6 @@ Given 2 functions (one for the `Ok` variant and one for the `Err` variant) execute the function that matches the `ResultAsync` variant. | ||
.andThen(insertUser) | ||
.match((user: User) => `User ${user.name} has been successfully created`, | ||
(e: Error) => `User could not be created because ${e.message}`) | ||
.match( | ||
(user: User) => `User ${user.name} has been successfully created`, | ||
(e: Error) => `User could not be created because ${e.message}` | ||
) | ||
@@ -792,2 +746,41 @@ // resultMessage is a string | ||
[⬆️ Back to top](#toc) | ||
--- | ||
### Utilities | ||
#### `combine` | ||
If you're familiar with `Promise.all`, the combine function works conceptually the same. | ||
The combine function takes a list of results and return a single result. If all the results in the list are `Ok`, then the return value will be a `Ok` containing a list of all the individual `Ok` values. | ||
If just one of the results in the list is an `Err` then the combine function returns that Err value (it short circuits and returns the first Err that it finds). | ||
Formally speaking: | ||
```typescript | ||
function combine<T, E>(resultList: Result<T, E>[]): Result<T[], E> | ||
``` | ||
Additionally, this same function also works for `ResultAsync`. And thanks to typescript function overloading, the types can be distinguished. | ||
```typescript | ||
function combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[], E> | ||
``` | ||
**Limitation**: `combine`, as you may have noticed, only works homogenous lists (where all the `T` and `E`'s are the same for each element in the list). There are plans to eventually make `combine` work on heterogeneous lists. | ||
[⬆️ Back to top](#toc) | ||
--- | ||
If you find this package useful, please consider [sponsoring me](https://github.com/sponsors/supermacro/) or simply [buying me a coffee](https://ko-fi.com/gdelgado)! | ||
--- | ||
## A note on the Package Name | ||
@@ -794,0 +787,0 @@ |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
48094
589
761