neverthrow
Advanced tools
Comparing version 2.2.1 to 2.3.0
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
@@ -37,3 +38,2 @@ }); | ||
}; | ||
var _this = this; | ||
exports.__esModule = true; | ||
@@ -51,3 +51,3 @@ var toPromise = function (val) { | ||
}; | ||
exports.chain = function (r1, r2) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain = function (r1, r2) { return __awaiter(void 0, void 0, void 0, function () { | ||
var inner, mapped; | ||
@@ -66,3 +66,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain3 = function (r1, r2, r3) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain3 = function (r1, r2, r3) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r2Promise, chained, mapped; | ||
@@ -83,3 +83,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain4 = function (r1, r2, r3, r4) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain4 = function (r1, r2, r3, r4) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r3Promise, chained, mapped; | ||
@@ -100,3 +100,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain5 = function (r1, r2, r3, r4, r5) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain5 = function (r1, r2, r3, r4, r5) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r4Promise, chained, mapped; | ||
@@ -117,3 +117,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain6 = function (r1, r2, r3, r4, r5, r6) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain6 = function (r1, r2, r3, r4, r5, r6) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r5Promise, chained, mapped; | ||
@@ -134,3 +134,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain7 = function (r1, r2, r3, r4, r5, r6, r7) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain7 = function (r1, r2, r3, r4, r5, r6, r7) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r6Promise, chained, mapped; | ||
@@ -151,3 +151,3 @@ return __generator(this, function (_a) { | ||
}); }; | ||
exports.chain8 = function (r1, r2, r3, r4, r5, r6, r7, r8) { return __awaiter(_this, void 0, void 0, function () { | ||
exports.chain8 = function (r1, r2, r3, r4, r5, r6, r7, r8) { return __awaiter(void 0, void 0, void 0, function () { | ||
var r7Promise, chained, mapped; | ||
@@ -154,0 +154,0 @@ return __generator(this, function (_a) { |
export { Result, ok, Ok, err, Err } from './result'; | ||
export { ResultAsync, okAsync, errAsync } from './result-async'; | ||
export { chain, chain3, chain4, chain5, chain6, chain7, chain8 } from './chain'; |
@@ -8,2 +8,6 @@ "use strict"; | ||
exports.Err = result_1.Err; | ||
var result_async_1 = require("./result-async"); | ||
exports.ResultAsync = result_async_1.ResultAsync; | ||
exports.okAsync = result_async_1.okAsync; | ||
exports.errAsync = result_async_1.errAsync; | ||
var chain_1 = require("./chain"); | ||
@@ -10,0 +14,0 @@ exports.chain = chain_1.chain; |
@@ -0,1 +1,2 @@ | ||
import { ResultAsync } from './'; | ||
export declare type Result<T, E> = Ok<T, E> | Err<T, E>; | ||
@@ -11,4 +12,5 @@ export declare const ok: <T, E>(value: T) => Ok<T, E>; | ||
mapErr<U>(_f: (e: E) => U): Result<T, U>; | ||
andThen<U>(f: (t: T) => ResultAsync<U, E>): ResultAsync<U, E>; | ||
andThen<U>(f: (t: T) => Result<U, E>): Result<U, E>; | ||
asyncMap<U>(f: (t: T) => Promise<U>): Promise<Result<U, E>>; | ||
asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E>; | ||
match: <A>(ok: (t: T) => A, _err: (e: E) => A) => A; | ||
@@ -26,3 +28,4 @@ _unsafeUnwrap(): T; | ||
andThen<U>(_f: (t: T) => Result<U, E>): Result<U, E>; | ||
asyncMap<U>(_f: (t: T) => Promise<U>): Promise<Result<U, E>>; | ||
andThen<U>(_f: (t: T) => ResultAsync<U, E>): Result<U, E>; | ||
asyncMap<U>(_f: (t: T) => Promise<U>): ResultAsync<U, E>; | ||
match: <A>(_ok: (t: T) => A, err: (e: E) => A) => A; | ||
@@ -29,0 +32,0 @@ _unsafeUnwrap(): T; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
exports.__esModule = true; | ||
var _1 = require("./"); | ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define | ||
@@ -64,5 +30,2 @@ exports.ok = function (value) { return new Ok(value); }; | ||
}; | ||
// add info on how this is really useful for converting a | ||
// Result<Result<T, E2>, E1> | ||
// into a Result<T, E2> | ||
Ok.prototype.andThen = function (f) { | ||
@@ -72,13 +35,3 @@ return f(this.value); | ||
Ok.prototype.asyncMap = function (f) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var newInner; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, f(this.value)]; | ||
case 1: | ||
newInner = _a.sent(); | ||
return [2 /*return*/, exports.ok(newInner)]; | ||
} | ||
}); | ||
}); | ||
return _1.ResultAsync.fromPromise(f(this.value)); | ||
}; | ||
@@ -121,7 +74,3 @@ Ok.prototype._unsafeUnwrap = function () { | ||
Err.prototype.asyncMap = function (_f) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, exports.err(this.error)]; | ||
}); | ||
}); | ||
return _1.errAsync(this.error); | ||
}; | ||
@@ -128,0 +77,0 @@ Err.prototype._unsafeUnwrap = function () { |
{ | ||
"name": "neverthrow", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "Stop throwing errors, and instead return Results!", | ||
@@ -26,16 +26,17 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@babel/core": "^7.4.3", | ||
"@babel/preset-env": "^7.4.3", | ||
"@babel/preset-typescript": "^7.3.3", | ||
"@types/jest": "^24.0.11", | ||
"@typescript-eslint/eslint-plugin": "^2.21.0", | ||
"@typescript-eslint/parser": "^2.21.0", | ||
"babel-jest": "^24.7.1", | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.10.0", | ||
"eslint-plugin-prettier": "^3.1.2", | ||
"jest": "^24.7.1", | ||
"prettier": "^1.19.1", | ||
"ts-jest": "^24.0.2", | ||
"typescript": "3.4.2" | ||
"@babel/core": "7.4.3", | ||
"@babel/preset-env": "7.4.3", | ||
"@babel/preset-typescript": "7.3.3", | ||
"@types/jest": "24.0.11", | ||
"@types/node": "^14.0.4", | ||
"@typescript-eslint/eslint-plugin": "2.21.0", | ||
"@typescript-eslint/parser": "2.21.0", | ||
"babel-jest": "24.7.1", | ||
"eslint": "6.8.0", | ||
"eslint-config-prettier": "6.10.0", | ||
"eslint-plugin-prettier": "3.1.2", | ||
"jest": "24.7.1", | ||
"prettier": "1.19.1", | ||
"ts-jest": "24.0.2", | ||
"typescript": "3.8.3" | ||
}, | ||
@@ -42,0 +43,0 @@ "keywords": [ |
460
README.md
@@ -1,7 +0,5 @@ | ||
# NeverThrow 🙅 | ||
[![Build Status](https://travis-ci.com/gDelgado14/neverthrow.svg?branch=master)](https://travis-ci.com/gDelgado14/neverthrow) | ||
[![Build Status](https://travis-ci.com/supermacro/neverthrow.svg?branch=master)](https://travis-ci.com/supermacro/neverthrow) | ||
## Description | ||
@@ -13,6 +11,8 @@ | ||
`neverthrow` also exposes an api for chaining sequential asynchronous tasks ([docs below](#chaining-api)). | ||
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. | ||
[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*. | ||
`neverthrow` also exposes `chain(...)` methods for chaining asynchronous tasks in a functional style ([docs below](#chaining-api)). However, these methods might be deprecated in the future. It is advised to use the `ResultAsync` instead. | ||
[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_. | ||
@@ -23,3 +23,2 @@ 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 | ||
@@ -33,8 +32,6 @@ | ||
## Usage | ||
### Synchronous API | ||
## Usage | ||
Create `Ok` or `Err` instances with the `ok` and `err` functions. | ||
@@ -65,9 +62,6 @@ | ||
`Result` is defined as follows: | ||
```typescript | ||
type Result<T, E> | ||
= Ok<T, E> | ||
| Err<T, E> | ||
type Result<T, E> = Ok<T, E> | Err<T, E> | ||
``` | ||
@@ -79,2 +73,61 @@ | ||
--- | ||
### 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 | ||
@@ -89,2 +142,5 @@ | ||
- `Result` type - only available in TypeScript | ||
- `ResultAsync` class | ||
- `okAsync` convenience function to create a `ResultAsync` containing an `Ok` type `Result` | ||
- `errAsync` convenience function to create a `ResultAsync` containing an `Err` type `Result` | ||
- `chain` and all of its variants ([docs below](#chaining-api)) - for chaining sequential asynchronous operations that return `Result`s | ||
@@ -96,15 +152,5 @@ | ||
// chain api available as well | ||
import { | ||
chain, | ||
chain3, | ||
chain4, | ||
chain5, | ||
chain6, | ||
chain7, | ||
chain8 | ||
} from 'neverthrow' | ||
import { chain, chain3, chain4, chain5, chain6, chain7, chain8 } from 'neverthrow' | ||
``` | ||
## Accessing the value inside of a Result | ||
@@ -121,3 +167,2 @@ | ||
const example1 = ok(123) | ||
@@ -132,3 +177,2 @@ const example2 = err('abc') | ||
if (example2.isErr()) { | ||
@@ -141,3 +185,2 @@ // you now have access to example2.error | ||
## API | ||
@@ -150,2 +193,3 @@ | ||
**Signature:** | ||
```typescript | ||
@@ -156,2 +200,3 @@ ok<T, E>(value: T): Ok<T, E> { ... } | ||
**Example:** | ||
```typescript | ||
@@ -173,2 +218,3 @@ import { ok } from 'neverthrow' | ||
**Signature:** | ||
```typescript | ||
@@ -179,2 +225,3 @@ err<T, E>(err: E): Err<T, E> { ... } | ||
**Example:** | ||
```typescript | ||
@@ -189,3 +236,2 @@ import { err } from 'neverthrow' | ||
--- | ||
@@ -198,2 +244,3 @@ | ||
**Signature:** | ||
```typescript | ||
@@ -210,2 +257,3 @@ isOk(): boolean { ... } | ||
**Signature**: | ||
```typescript | ||
@@ -215,6 +263,4 @@ isErr(): boolean { ... } | ||
--- | ||
### `Result.map` (method) | ||
@@ -227,2 +273,3 @@ | ||
**Signature:** | ||
```typescript | ||
@@ -233,4 +280,4 @@ type MapFunc = <T>(f: T) => U | ||
**Example**: | ||
**Example**: | ||
```typescript | ||
@@ -254,3 +301,3 @@ const { getLines } from 'imaginary-parser' | ||
--- | ||
--- | ||
@@ -264,2 +311,3 @@ ### `Result.mapErr` (method) | ||
**Signature:** | ||
```typescript | ||
@@ -270,5 +318,4 @@ type MapFunc = <E>(e: E) => F | ||
**Example**: | ||
**Example**: | ||
```typescript | ||
@@ -296,4 +343,7 @@ import { parseHeaders } 'imaginary-http-parser' | ||
Same idea as `map` above. Except you must return a new `Result`. | ||
Same idea as `map` above. Except you must return a new `Result` or `ResultAsync`. | ||
If the provided method returns a `Result` the returned value will be a `Result`. | ||
If the provided method returns a `ResultAsync` the returned value will be a `ResultAsync`. | ||
This is useful for when you need to do a subsequent computation using the inner `T` value, but that computation might fail. | ||
@@ -304,9 +354,15 @@ | ||
**Signature:** | ||
```typescript | ||
type ExtendFunc = (t: T) => Result<U, E> | ||
extend<U>(f: ExtendFunc): Result<U, E> { ... } | ||
type AndThenFunc = (t: T) => Result<U, E> | ||
andThen<U>(f: AndThenFunc): Result<U, E> { ... } | ||
type AndThenAsyncFunc = (t: T) => ResultAsync<U, E> | ||
andThen<U>(f: AndThenAsyncFunc): ResultAsync<U, E> { ... } | ||
``` | ||
**Example 1: Chaining Results** | ||
**Example 1: Chaining Results** | ||
```typescript | ||
@@ -317,12 +373,19 @@ import { err, ok } from 'neverthrow' | ||
ok(2).andThen(sq).andThen(sq) // Ok(16) | ||
ok(2) | ||
.andThen(sq) | ||
.andThen(sq) // Ok(16) | ||
ok(2).andThen(sq).andThen(err) // Err(4) | ||
ok(2) | ||
.andThen(sq) | ||
.andThen(err) // Err(4) | ||
ok(2).andThen(err).andThen(sq) // Err(2) | ||
ok(2) | ||
.andThen(err) | ||
.andThen(sq) // Err(2) | ||
err(3).andThen(sq).andThen(sq) // Err(3) | ||
err(3) | ||
.andThen(sq) | ||
.andThen(sq) // Err(3) | ||
``` | ||
**Example 2: Flattening Nested Results** | ||
@@ -338,3 +401,2 @@ | ||
--- | ||
@@ -349,2 +411,3 @@ | ||
**Signature:** | ||
```typescript | ||
@@ -357,6 +420,4 @@ match<A>( | ||
`match` is like chaining `map` and `mapErr`, with the distinction that with `match` both functions must have the same return type. | ||
**Example:** | ||
@@ -379,17 +440,10 @@ | ||
// error handling | ||
result | ||
.map(successCallback) | ||
.mapErr(failureCallback) | ||
result.map(successCallback).mapErr(failureCallback) | ||
// match api | ||
// works exactly the same as above, | ||
// except, now you HAVE to do error handling :) | ||
myval.match( | ||
successCallback, | ||
failureCallback | ||
) | ||
myval.match(successCallback, failureCallback) | ||
``` | ||
--- | ||
@@ -400,6 +454,7 @@ | ||
Similar to `map` except for two things: | ||
- the mapping function must return a `Promise` | ||
- asyncMap returns a `Promise` | ||
- asyncMap returns a `ResultAsync` | ||
**Check out the `chain` API**. If you need to chain multiple `Promise<Result>` type of computations together, `chain` is what you need. `asyncMap` is only suitable for doing a single async computation, not many sequential computations. | ||
You can then chain the result of `asyncMap` using the `ResultAsync` apis (like `map`, `mapErr`, `andThen`, etc.) | ||
@@ -410,3 +465,3 @@ **Signature:** | ||
type MappingFunc = (t: T) => Promise<U> | ||
asyncMap<U>(fn: MappingFunc): Promise<Result<U, E>> { ... } | ||
asyncMap<U>(fn: MappingFunc): ResultAsync<U, E> { ... } | ||
``` | ||
@@ -421,3 +476,3 @@ | ||
const promise = parseHeaders(rawHeader) | ||
const asyncRes = parseHeaders(rawHeader) | ||
.map(headerKvMap => headerKvMap.Authorization) | ||
@@ -427,8 +482,263 @@ .asyncMap(findUserInDatabase) | ||
Note that in the above example if `parseHeaders` returns an `Err` then `.map` and `.asyncMap` will not be invoked, and `promise` variable will contain a `Err` inside of the promise. | ||
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()`. | ||
--- | ||
### `okAsync` | ||
Constructs an `Ok` variant of `ResultAsync` | ||
**Signature:** | ||
```typescript | ||
okAsync<T, E>(value: T): ResultAsync<T, E> | ||
``` | ||
**Example:** | ||
```typescript | ||
import { okAsync } from 'neverthrow' | ||
const myResultAsync = okAsync({ myData: 'test' }) // instance of `ResultAsync` | ||
const myResult = await myResultAsync // instance of `Ok` | ||
myResult.isOk() // true | ||
myResult.isErr() // false | ||
``` | ||
--- | ||
### `errAsync` | ||
Constructs an `Err` variant of `ResultAsync` | ||
**Signature:** | ||
```typescript | ||
errAsync<T, E>(err: E): ResultAsync<T, E> | ||
``` | ||
**Example:** | ||
```typescript | ||
import { errAsync } from 'neverthrow' | ||
const myResultAsync = errAsync('Oh nooo') // instance of `ResultAsync` | ||
const myResult = await myResultAsync // instance of `Err` | ||
myResult.isOk() // false | ||
myResult.isErr() // true | ||
``` | ||
--- | ||
### `ResultAsync.fromPromise` (method) | ||
Transforms a `Promise<T>` into a `ResultAsync<T, E>`. | ||
The second argument handles the rejection case of the promise. If it is ommited, **the code might throw** because `neverthrow` does not know if the promise you are passing to `fromPromise` has any promise rejection logic associated to it (via a `.catch` method call or `catch (err) {}` block). | ||
**Signature:** | ||
```typescript | ||
fromPromise<U, E>(p: Promise<U>, f?: (e: unknown) => E): ResultAsync<U, E> { ... } | ||
``` | ||
**Example**: | ||
```typescript | ||
import { insertIntoDb } from 'imaginary-database' | ||
// insertIntoDb(user: User): Promise<User> | ||
const res = ResultAsync.fromPromise(insertIntoDb(myUser), () => new Error('Database error')) | ||
// res has a type of ResultAsync<User, Error> | ||
``` | ||
--- | ||
### `ResultAsync.map` (method) | ||
Maps a `ResultAsync<T, E>` to `ResultAsync<U, E>` by applying a function to a contained `Ok` value, leaving an `Err` value untouched. | ||
The applied function can be synchronous or asynchronous (returning a `Promise<U>`) with no impact to the return type. | ||
This function can be used to compose the results of two functions. | ||
**Signature:** | ||
```typescript | ||
type MapFunc = <T>(f: T | Promise<T>) => U | ||
map<U>(fn: MapFunc): ResultAsync<U, E> { ... } | ||
``` | ||
**Example**: | ||
```typescript | ||
const { findUsersIn } from 'imaginary-database' | ||
// ^ assume findUsersIn has the following signature: | ||
// findUsersIn(country: string): ResultAsync<Array<User>, Error> | ||
const usersInCanada = findUsersIn("Canada") | ||
// Let's assume we only need their names | ||
const namesInCanada = usersInCanada.map((users: Array<User>) => users.map(user => user.name)) | ||
// namesInCanada is of type ResultAsync<Array<string>, Error> | ||
// We can extract the Result using .then() or await | ||
namesInCanada.then((namesResult: Result<Array<string>, Error>) => { | ||
if(namesResult.isErr()){ | ||
console.log("Couldn't get the users from the database", namesResult.error) | ||
} | ||
else{ | ||
console.log("Users in Canada are named: " + namesResult.value.join(',')) | ||
} | ||
}) | ||
``` | ||
--- | ||
### `ResultAsync.mapErr` (method) | ||
Maps a `ResultAsync<T, E>` to `ResultAsync<T, F>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched. | ||
The applied function can be synchronous or asynchronous (returning a `Promise<F>`) with no impact to the return type. | ||
This function can be used to pass through a successful result while handling an error. | ||
**Signature:** | ||
```typescript | ||
type MapFunc = <E>(e: E) => F | Promise<F> | ||
mapErr<U>(fn: MapFunc): ResultAsync<T, F> { ... } | ||
``` | ||
**Example**: | ||
```typescript | ||
const { findUsersIn } from 'imaginary-database' | ||
// ^ assume findUsersIn has the following signature: | ||
// findUsersIn(country: string): ResultAsync<Array<User>, Error> | ||
// Let's say we need to low-level errors from findUsersIn to be more readable | ||
const usersInCanada = findUsersIn("Canada").mapErr((e: Error) => { | ||
// The only error we want to pass to the user is "Unknown country" | ||
if(e.message === "Unknown country"){ | ||
return e.message | ||
} | ||
// All other errors will be labelled as a system error | ||
return "System error, please contact an administrator." | ||
}) | ||
// usersInCanada is of type ResultAsync<Array<User>, string> | ||
usersInCanada.then((usersResult: Result<Array<User>, string>) => { | ||
if(usersResult.isErr()){ | ||
res.status(400).json({ | ||
error: usersResult.error | ||
}) | ||
} | ||
else{ | ||
res.status(200).json({ | ||
users: usersResult.value | ||
}) | ||
} | ||
}) | ||
``` | ||
--- | ||
### `ResultAsync.andThen` (method) | ||
Same idea as `map` above. Except the applied function must return a `Result` or `ResultAsync`. | ||
`ResultAsync.andThen` always returns a `ResultAsync` no matter the return type of the applied function. | ||
This is useful for when you need to do a subsequent computation using the inner `T` value, but that computation might fail. | ||
`andThen` is really useful as a tool to flatten a `ResultAsync<ResultAsync<A, E2>, E1>` into a `ResultAsync<A, E2>` (see example below). | ||
**Signature:** | ||
```typescript | ||
type AndThenFunc = (t: T) => ResultAsync<U, E> | Result<U, E> | ||
andThen<U>(f: AndThenFunc): ResultAsync<U, E> { ... } | ||
``` | ||
**Example** | ||
```typescript | ||
const { validateUser } from 'imaginary-validator' | ||
const { insertUser } from 'imaginary-database' | ||
const { sendNotification } from 'imaginary-service' | ||
// ^ assume validateUser, insertUser and sendNotification have the following signatures: | ||
// validateUser(user: User): Result<User, Error> | ||
// insertUser(user): ResultAsync<User, Error> | ||
// sendNotification(user): ResultAsync<void, Error> | ||
const resAsync = validateUser(user) | ||
.andThen(insertUser) | ||
.andThen(sendNotification) | ||
// resAsync is a ResultAsync<void, Error> | ||
resAsync.then((res: Result<void, Error>) => { | ||
if(res.isErr()){ | ||
console.log("Oops, at least one step failed", res.error) | ||
} | ||
else{ | ||
console.log("User has been validated, inserted and notified successfully.") | ||
} | ||
}) | ||
``` | ||
--- | ||
### `ResultAsync.match` (method) | ||
Given 2 functions (one for the `Ok` variant and one for the `Err` variant) execute the function that matches the `ResultAsync` variant. | ||
The difference with `Result.match` is that it always returns a `Promise` because of the asynchronous nature of the `ResultAsync`. | ||
**Signature:** | ||
```typescript | ||
match<A>( | ||
okFn: (t: T) => A, | ||
errFn: (e: E) => A | ||
): Promise<A> => { ... } | ||
``` | ||
**Example:** | ||
```typescript | ||
const { validateUser } from 'imaginary-validator' | ||
const { insertUser } from 'imaginary-database' | ||
// ^ assume validateUser and insertUser have the following signatures: | ||
// validateUser(user: User): Result<User, Error> | ||
// insertUser(user): ResultAsync<User, Error> | ||
// Handle both cases at the end of the chain using match | ||
const resultMessage = await validateUser(user) | ||
.andThen(insertUser) | ||
.match((user: User) => `User ${user.name} has been successfully created`, | ||
(e: Error) => `User could not be created because ${e.message}`) | ||
// resultMessage is a string | ||
``` | ||
--- | ||
# 🔗 | ||
## Chaining API | ||
> Disclaimer: the preferred solution to chaining asynchronous tasks is `ResultAsync`. | ||
> The following method might be deprecated in the future. | ||
tldr: `chain` is the `.andThen` equivalent for `Result`s wrapped inside of a `Promise`. | ||
@@ -461,13 +771,10 @@ | ||
// ... | ||
chain4( | ||
sessionManager.getSessionUser(), | ||
({ id }) => getSingleSite(id, siteId), | ||
fetchSiteWithComments, | ||
(siteWithComments) => Promise.resolve( | ||
ok(buildSite(siteWithComments)) | ||
), | ||
) | ||
chain4( | ||
sessionManager.getSessionUser(), | ||
({ id }) => getSingleSite(id, siteId), | ||
fetchSiteWithComments, | ||
siteWithComments => Promise.resolve(ok(buildSite(siteWithComments))), | ||
) | ||
``` | ||
### `chain` | ||
@@ -485,2 +792,3 @@ | ||
The above in plain english: | ||
- given a computation `r1` | ||
@@ -490,3 +798,2 @@ - evaluate `r2` with the `Ok` value of `r1` as r2`'s argument. | ||
### `chain3` | ||
@@ -521,3 +828,2 @@ | ||
### `chain5` | ||
@@ -539,3 +845,2 @@ | ||
### `chain6` | ||
@@ -558,3 +863,2 @@ | ||
### `chain7` | ||
@@ -578,3 +882,2 @@ | ||
### `chain8` | ||
@@ -601,6 +904,5 @@ | ||
## Wrapping a Dependency that throws | ||
> incomplete documenation ... | ||
> incomplete documenation ... | ||
> Examples to come soon | ||
@@ -611,6 +913,2 @@ | ||
## A note on the Package Name | ||
@@ -617,0 +915,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
49608
11
437
878
15
2