Showing preview only (241K chars total). Download the full file or copy to clipboard to get everything.
Repository: supermacro/neverthrow
Branch: master
Commit: 5ef3a018bda7
Files: 24
Total size: 232.0 KB
Directory structure:
gitextract_7c6yg_7y/
├── .babelrc
├── .changeset/
│ └── config.json
├── .eslintrc.js
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .prettierrc.js
├── CHANGELOG.md
├── CODEOWNERS
├── LICENSE
├── README.md
├── jest.config.js
├── package.json
├── rollup.config.mjs
├── src/
│ ├── _internals/
│ │ ├── error.ts
│ │ └── utils.ts
│ ├── index.ts
│ ├── result-async.ts
│ └── result.ts
├── tests/
│ ├── index.test.ts
│ ├── safe-try.test.ts
│ ├── tsconfig.tests.json
│ └── typecheck-tests.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "supermacro/neverthrow"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
env: {
node: true,
},
ignorePatterns: ['dist/', 'tests/'],
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {
semi: 'off',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/semi': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/member-delimiter-style': ['error', {
multiline: {
delimiter: 'none',
},
singleline: {
delimiter: 'semi',
requireLast: false
}
}]
},
};
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
RUNNER_NODE_VERSION: 22
jobs:
install_deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.RUNNER_NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.RUNNER_NODE_VERSION }}
- name: cache dependencies
uses: actions/cache@v4
id: cache-dependencies
with:
path: "node_modules"
key: depends-node${{ env.RUNNER_NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: npm i
typecheck:
runs-on: ubuntu-latest
needs: install_deps
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.RUNNER_NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.RUNNER_NODE_VERSION }}
- name: restore dependencies cache
uses: actions/cache/restore@v4
with:
path: "node_modules"
key: depends-node${{ env.RUNNER_NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: typecheck
run: npm run typecheck
lint:
runs-on: ubuntu-latest
needs: install_deps
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.RUNNER_NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.RUNNER_NODE_VERSION }}
- name: restore dependencies cache
uses: actions/cache/restore@v4
with:
path: "node_modules"
key: depends-node${{ env.RUNNER_NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: lint
run: npm run lint
build:
runs-on: ubuntu-latest
needs: install_deps
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.RUNNER_NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.RUNNER_NODE_VERSION }}
- name: restore dependencies cache
uses: actions/cache/restore@v4
with:
path: "node_modules"
key: depends-node${{ env.RUNNER_NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: build
run: npm run build
test:
runs-on: ubuntu-latest
needs: install_deps
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: restore dependencies cache
uses: actions/cache/restore@v4
with:
path: "node_modules"
key: depends-node${{ env.RUNNER_NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: test
run: npm run test
test_result:
runs-on: ubuntu-latest
needs: test
if: ${{ always() }}
steps:
- run: exit 1
if: ${{ needs.test.result != 'success' }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
branches:
- master
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
create_pr:
name: Release
runs-on: ubuntu-latest
outputs:
hasChangesets: ${{ steps.changesets.outputs.hasChangesets }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm i
- name: Create Release Pull Request
id: changesets
uses: changesets/action@v1
with:
version: npm run version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release:
needs: create_pr
if: needs.create_pr.outputs.hasChangesets == 'false'
runs-on: ubuntu-latest
environment: deploy
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm i
- name: release
id: changesets
uses: changesets/action@v1
with:
publish: npm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .gitignore
================================================
dist/
tmp/
node_modules/
publish.sh
================================================
FILE: .prettierrc.js
================================================
module.exports = {
semi: false,
trailingComma: "all",
singleQuote: true,
printWidth: 100,
tabWidth: 2
};
================================================
FILE: CHANGELOG.md
================================================
# neverthrow
## 8.2.0
### Minor Changes
- [#615](https://github.com/supermacro/neverthrow/pull/615) [`85ed7fd`](https://github.com/supermacro/neverthrow/commit/85ed7fd3a1247e4c0e83bba13f5e874282243d75) Thanks [@konker](https://github.com/konker)! - Add orTee, which is the equivalent of andTee but for the error track.
- [#584](https://github.com/supermacro/neverthrow/pull/584) [`acea44a`](https://github.com/supermacro/neverthrow/commit/acea44adb98dda2ca32fe4e882879461cc7cedc2) Thanks [@macksal](https://github.com/macksal)! - Allow ok/err/okAsync/errAsync to accept zero arguments when returning void
## 8.1.1
### Patch Changes
- [#600](https://github.com/supermacro/neverthrow/pull/600) [`3aee20a`](https://github.com/supermacro/neverthrow/commit/3aee20a1c429062d26f440fde32a3f26ef05533a) Thanks [@m-shaka](https://github.com/m-shaka)! - docs: updated README.md about `safeTry` and added @deprecated tag to safeUnwrap
## 8.1.0
### Minor Changes
- [#589](https://github.com/supermacro/neverthrow/pull/589) [`609b398`](https://github.com/supermacro/neverthrow/commit/609b398aa1fd258a1fede974707d54eb4c230f3c) Thanks [@dmmulroy](https://github.com/dmmulroy)! - safeTry should not require .safeUnwrap()
## 8.0.0
### Major Changes
- [#484](https://github.com/supermacro/neverthrow/pull/484) [`09faf35`](https://github.com/supermacro/neverthrow/commit/09faf35a5ce701ed55b13b82074da9e50050526d) Thanks [@braxtonhall](https://github.com/braxtonhall)! - Allow orElse method to change ok types.
This makes the orElse types match the implementation.
This is a breaking change for the orElse type argument list,
as the ok type must now be provided before the err type.
```diff
- result.orElse<ErrType>(foo)
+ result.orElse<OkType, ErrType>(foo)
```
This only applies if type arguments were
explicitly provided at an orElse callsite.
If the type arguments were inferred,
no updates are needed during the upgrade.
## 7.2.0
### Minor Changes
- [#562](https://github.com/supermacro/neverthrow/pull/562) [`547352f`](https://github.com/supermacro/neverthrow/commit/547352f326206b2c5b403bde4ddc88825172f25c) Thanks [@sharno](https://github.com/sharno)! - change the return type of `safeTry` to be `ResultAsync<T, E>` instead of `Promise<Result<T, E>>` for better composability
## 7.1.0
### Minor Changes
- [#467](https://github.com/supermacro/neverthrow/pull/467) [`4b9d2fd`](https://github.com/supermacro/neverthrow/commit/4b9d2fdaf03223945068509f948b57194732aa03) Thanks [@untidy-hair
](https://github.com/untidy-hair)! - feat: add `andTee` and `andThrough` to handle side-effect
### Patch Changes
- [#483](https://github.com/supermacro/neverthrow/pull/483) [`96f7f66`](https://github.com/supermacro/neverthrow/commit/96f7f669ac83be705a389d47ed804e9d44a13932) Thanks [@braxtonhall](https://github.com/braxtonhall)! - Fix `combineWithAllErrors` types
- [#563](https://github.com/supermacro/neverthrow/pull/563) [`eadf50c`](https://github.com/supermacro/neverthrow/commit/eadf50c695db896b8841c0ee301ae5eeba994b90) Thanks [@mattpocock](https://github.com/mattpocock)! - Made err() infer strings narrowly for easier error tagging.
## 7.0.1
### Patch Changes
- [#527](https://github.com/supermacro/neverthrow/pull/527) [`2e1f198`](https://github.com/supermacro/neverthrow/commit/2e1f19899800ce5e1164412c6a693cf2f1c40b20) Thanks [@3846masa](https://github.com/3846masa)! - fix: change type definitions to make inferring types of safeTry more strict
- [#497](https://github.com/supermacro/neverthrow/pull/497) [`e06203e`](https://github.com/supermacro/neverthrow/commit/e06203e90b2b64edaa42707cbca8383c9f4765e8) Thanks [@braxtonhall](https://github.com/braxtonhall)! - enhance type inferrence of `match`
## 7.0.0
### Major Changes
- [#553](https://github.com/supermacro/neverthrow/pull/553) [`5a3af0a`](https://github.com/supermacro/neverthrow/commit/5a3af0a55d0c440dfd50bfbbe021c6e4b973184b) Thanks [@m-shaka](https://github.com/m-shaka)! - Declare the minimum supported Node.js version
`Neverthrow` does not depend on any Node.js version-specific features, so it should work with any version of Node.js that supports ES6 and other runtimes like Browser, Deno, etc.
However, for the sake of maintaining a consistent development environment, we should declare the minimum supported version of Node.js in the `engines` field of the `package.json` file.
================================================
FILE: CODEOWNERS
================================================
* @m-shaka @supermacro
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Giorgio Delgado
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# NeverThrow 🙅
[](https://github.com/supermacro/neverthrow/actions)
## Description
Encode failure into your program.

This package contains a `Result` type that represents either success (`Ok`) or failure (`Err`).
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>`.
`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.
> Need to see real-life examples of how to leverage this package for error handling? See this repo: https://github.com/parlez-vous/server
<div id="toc"></div>
## Table Of Contents
* [Installation](#installation)
* [Recommended: Use `eslint-plugin-neverthrow`](#recommended-use-eslint-plugin-neverthrow)
* [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.orElse` (method)](#resultorelse-method)
- [`Result.match` (method)](#resultmatch-method)
- [`Result.asyncMap` (method)](#resultasyncmap-method)
- [`Result.andTee` (method)](#resultandtee-method)
- [`Result.orTee` (method)](#resultortee-method)
- [`Result.andThrough` (method)](#resultandthrough-method)
- [`Result.asyncAndThrough` (method)](#resultasyncandthrough-method)
- [`Result.fromThrowable` (static class method)](#resultfromthrowable-static-class-method)
- [`Result.combine` (static class method)](#resultcombine-static-class-method)
- [`Result.combineWithAllErrors` (static class method)](#resultcombinewithallerrors-static-class-method)
- [`Result.safeUnwrap()`](#resultsafeunwrap)
+ [Asynchronous API (`ResultAsync`)](#asynchronous-api-resultasync)
- [`okAsync`](#okasync)
- [`errAsync`](#errasync)
- [`ResultAsync.fromThrowable` (static class method)](#resultasyncfromthrowable-static-class-method)
- [`ResultAsync.fromPromise` (static class method)](#resultasyncfrompromise-static-class-method)
- [`ResultAsync.fromSafePromise` (static class method)](#resultasyncfromsafepromise-static-class-method)
- [`ResultAsync.map` (method)](#resultasyncmap-method)
- [`ResultAsync.mapErr` (method)](#resultasyncmaperr-method)
- [`ResultAsync.unwrapOr` (method)](#resultasyncunwrapor-method)
- [`ResultAsync.andThen` (method)](#resultasyncandthen-method)
- [`ResultAsync.orElse` (method)](#resultasyncorelse-method)
- [`ResultAsync.match` (method)](#resultasyncmatch-method)
- [`ResultAsync.andTee` (method)](#resultasyncandtee-method)
- [`ResultAsync.orTee` (method)](#resultasyncortee-method)
- [`ResultAsync.andThrough` (method)](#resultasyncandthrough-method)
- [`ResultAsync.combine` (static class method)](#resultasynccombine-static-class-method)
- [`ResultAsync.combineWithAllErrors` (static class method)](#resultasynccombinewithallerrors-static-class-method)
- [`ResultAsync.safeUnwrap()`](#resultasyncsafeunwrap)
+ [Utilities](#utilities)
- [`fromThrowable`](#fromthrowable)
- [`fromAsyncThrowable`](#fromasyncthrowable)
- [`fromPromise`](#frompromise)
- [`fromSafePromise`](#fromsafepromise)
- [`safeTry`](#safetry)
+ [Testing](#testing)
* [A note on the Package Name](#a-note-on-the-package-name)
## Installation
```sh
> npm install neverthrow
```
## Recommended: Use `eslint-plugin-neverthrow`
As part of `neverthrow`s [bounty program](https://github.com/supermacro/neverthrow/issues/314), user [mdbetancourt](https://github.com/mdbetancourt) created [`eslint-plugin-neverthrow`](https://github.com/mdbetancourt/eslint-plugin-neverthrow) to ensure that errors are not gone unhandled.
Install by running:
```sh
> npm install eslint-plugin-neverthrow
```
With `eslint-plugin-neverthrow`, you are forced to consume the result in one of the following three ways:
- Calling `.match`
- Calling `.unwrapOr`
- Calling `._unsafeUnwrap`
This ensures that you're explicitly handling the error of your `Result`.
This plugin is essentially a porting of Rust's [`must-use`](https://doc.rust-lang.org/std/result/#results-must-be-used) attribute.
## Top-Level API
`neverthrow` exposes the following:
- `ok` convenience function to create an `Ok` variant of `Result`
- `err` convenience function to create an `Err` variant of `Result`
- `Ok` class and type
- `Err` class and type
- `Result` Type as well as namespace / object from which to call [`Result.fromThrowable`](#resultfromthrowable-static-class-method), [Result.combine](#resultcombine-static-class-method).
- `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`
```typescript
import {
ok,
Ok,
err,
Err,
Result,
okAsync,
errAsync,
ResultAsync,
fromAsyncThrowable,
fromThrowable,
fromPromise,
fromSafePromise,
safeTry,
} from 'neverthrow'
```
---
**Check out the [wiki](https://github.com/supermacro/neverthrow/wiki) for help on how to make the most of `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)!
---
## API Documentation
### Synchronous API (`Result`)
#### `ok`
Constructs an `Ok` variant of `Result`
**Signature:**
```typescript
ok<T, E>(value: T): Ok<T, E> { ... }
```
**Example:**
```typescript
import { ok } from 'neverthrow'
const myResult = ok({ myData: 'test' }) // instance of `Ok`
myResult.isOk() // true
myResult.isErr() // false
```
[⬆️ Back to top](#toc)
---
#### `err`
Constructs an `Err` variant of `Result`
**Signature:**
```typescript
err<T, E>(error: E): Err<T, E> { ... }
```
**Example:**
```typescript
import { err } from 'neverthrow'
const myResult = err('Oh noooo') // instance of `Err`
myResult.isOk() // false
myResult.isErr() // true
```
[⬆️ Back to top](#toc)
---
#### `Result.isOk` (method)
Returns `true` if the result is an `Ok` variant
**Signature:**
```typescript
isOk(): boolean { ... }
```
[⬆️ Back to top](#toc)
---
#### `Result.isErr` (method)
Returns `true` if the result is an `Err` variant
**Signature**:
```typescript
isErr(): boolean { ... }
```
[⬆️ Back to top](#toc)
---
#### `Result.map` (method)
Maps a `Result<T, E>` to `Result<U, E>` by applying a function to a contained `Ok` value, leaving an `Err` value untouched.
This function can be used to compose the results of two functions.
**Signature:**
```typescript
class Result<T, E> {
map<U>(callback: (value: T) => U): Result<U, E> { ... }
}
```
**Example**:
```typescript
import { getLines } from 'imaginary-parser'
// ^ assume getLines has the following signature:
// getLines(str: string): Result<Array<string>, Error>
// since the formatting is deemed correct by `getLines`
// then it means that `linesResult` is an Ok
// containing an Array of strings for each line of code
const linesResult = getLines('1\n2\n3\n4\n')
// this Result now has a Array<number> inside it
const newResult = linesResult.map(
(arr: Array<string>) => arr.map(parseInt)
)
newResult.isOk() // true
```
[⬆️ Back to top](#toc)
---
#### `Result.mapErr` (method)
Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a contained `Err` value, leaving an `Ok` value untouched.
This function can be used to pass through a successful result while handling an error.
**Signature:**
```typescript
class Result<T, E> {
mapErr<F>(callback: (error: E) => F): Result<T, F> { ... }
}
```
**Example**:
```typescript
import { parseHeaders } from 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result<SomeKeyValueMap, ParseError>
const rawHeaders = 'nonsensical gibberish and badly formatted stuff'
const parseResult = parseHeaders(rawHeaders)
parseResult.mapErr(parseError => {
res.status(400).json({
error: parseError
})
})
parseResult.isErr() // true
```
[⬆️ Back to top](#toc)
---
#### `Result.unwrapOr` (method)
Unwrap the `Ok` value, or return the default if there is an `Err`
**Signature:**
```typescript
class Result<T, E> {
unwrapOr<T>(value: T): T { ... }
}
```
**Example**:
```typescript
const myResult = err('Oh noooo')
const multiply = (value: number): number => value * 2
const unwrapped: number = myResult.map(multiply).unwrapOr(10)
```
[⬆️ Back to top](#toc)
---
#### `Result.andThen` (method)
Same idea as `map` above. Except you must return a new `Result`.
The returned value will be a `Result`. As of `v4.1.0-beta`, you are able to return distinct error types (see signature below). Prior to `v4.1.0-beta`, the error type could not be distinct.
This is useful for when you need to do a subsequent computation using the inner `T` value, but that computation might fail.
Additionally, `andThen` is really useful as a tool to flatten a `Result<Result<A, E2>, E1>` into a `Result<A, E2>` (see example below).
**Signature:**
```typescript
class Result<T, E> {
// Note that the latest version lets you return distinct errors as well.
// If the error types (E and F) are the same (like `string | string`)
// then they will be merged into one type (`string`)
andThen<U, F>(
callback: (value: T) => Result<U, F>
): Result<U, E | F> { ... }
}
```
**Example 1: Chaining Results**
```typescript
import { err, ok } from 'neverthrow'
const sq = (n: number): Result<number, number> => ok(n ** 2)
ok(2)
.andThen(sq)
.andThen(sq) // Ok(16)
ok(2)
.andThen(sq)
.andThen(err) // Err(4)
ok(2)
.andThen(err)
.andThen(sq) // Err(2)
err(3)
.andThen(sq)
.andThen(sq) // Err(3)
```
**Example 2: Flattening Nested Results**
```typescript
// It's common to have nested Results
const nested = ok(ok(1234))
// notNested is a Ok(1234)
const notNested = nested.andThen((innerResult) => innerResult)
```
[⬆️ Back to top](#toc)
---
#### `Result.asyncAndThen` (method)
Same idea as [`andThen` above](#resultandthen-method), except you must return a new `ResultAsync`.
The returned value will be a `ResultAsync`.
**Signature:**
```typescript
class Result<T, E> {
asyncAndThen<U, F>(
callback: (value: T) => ResultAsync<U, F>
): ResultAsync<U, E | F> { ... }
}
```
[⬆️ Back to top](#toc)
---
#### `Result.orElse` (method)
Takes an `Err` value and maps it to a `Result<T, SomeNewType>`. This is useful for error recovery.
**Signature:**
```typescript
class Result<T, E> {
orElse<U, A>(
callback: (error: E) => Result<U, A>
): Result<U | T, A> { ... }
}
```
**Example:**
```typescript
enum DatabaseError {
PoolExhausted = 'PoolExhausted',
NotFound = 'NotFound',
}
const dbQueryResult: Result<string, DatabaseError> = err(DatabaseError.NotFound)
const updatedQueryResult = dbQueryResult.orElse((dbError) =>
dbError === DatabaseError.NotFound
? ok('User does not exist') // error recovery branch: ok() must be called with a value of type string
//
//
// err() can be called with a value of any new type that you want
// it could also be called with the same error value
//
// err(dbError)
: err(500)
)
```
[⬆️ Back to top](#toc)
---
#### `Result.match` (method)
Given 2 functions (one for the `Ok` variant and one for the `Err` variant) execute the function that matches the `Result` variant.
Match callbacks do not necessitate to return a `Result`, however you can return a `Result` if you want to.
**Signature:**
```typescript
class Result<T, E> {
match<A, B = A>(
okCallback: (value: T) => A,
errorCallback: (error: E) => B
): A | B => { ... }
}
```
`match` is like chaining `map` and `mapErr`, with the distinction that with `match` both functions must have the same return type.
The differences between `match` and chaining `map` and `mapErr` are that:
- with `match` both functions must have the same return type `A`
- `match` unwraps the `Result<T, E>` into an `A` (the match functions' return type)
- This makes no difference if you are performing side effects only
**Example:**
```typescript
// map/mapErr api
// note that you DON'T have to append mapErr
// after map which means that you are not required to do
// error handling
computationThatMightFail().map(console.log).mapErr(console.error)
// match api
// works exactly the same as above since both callbacks
// only perform side effects,
// except, now you HAVE to do error handling :)
computationThatMightFail().match(console.log, console.error)
// Returning values
const attempt = computationThatMightFail()
.map((str) => str.toUpperCase())
.mapErr((err) => `Error: ${err}`)
// `attempt` is of type `Result<string, string>`
const answer = computationThatMightFail().match(
(str) => str.toUpperCase(),
(err) => `Error: ${err}`
)
// `answer` is of type `string`
```
If you don't use the error parameter in your match callback then `match` is equivalent to chaining `map` with `unwrapOr`:
```ts
const answer = computationThatMightFail().match(
(str) => str.toUpperCase(),
() => 'ComputationError'
)
// `answer` is of type `string`
const answer = computationThatMightFail()
.map((str) => str.toUpperCase())
.unwrapOr('ComputationError')
```
[⬆️ Back to top](#toc)
---
#### `Result.asyncMap` (method)
Similar to `map` except for two things:
- the mapping function must return a `Promise`
- asyncMap returns a `ResultAsync`
You can then chain the result of `asyncMap` using the `ResultAsync` apis (like `map`, `mapErr`, `andThen`, etc.)
**Signature:**
```typescript
class Result<T, E> {
asyncMap<U>(
callback: (value: T) => Promise<U>
): ResultAsync<U, E> { ... }
}
```
**Example:**
```typescript
import { parseHeaders } from 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result<SomeKeyValueMap, ParseError>
const asyncRes = parseHeaders(rawHeader)
.map(headerKvMap => headerKvMap.Authorization)
.asyncMap(findUserInDatabase)
```
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)
---
#### `Result.andTee` (method)
Takes a `Result<T, E>` and lets the original `Result<T, E>` pass through regardless the result of the passed-in function.
This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
**Signature:**
```typescript
class Result<T, E> {
andTee(
callback: (value: T) => unknown
): Result<T, E> { ... }
}
```
**Example:**
```typescript
import { parseUserInput } from 'imaginary-parser'
import { logUser } from 'imaginary-logger'
import { insertUser } from 'imaginary-database'
// ^ assume parseUserInput, logUser and insertUser have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// logUser(user: User): Result<void, LogError>
// insertUser(user: User): ResultAsync<void, InsertError>
// Note logUser returns void upon success but insertUser takes User type.
const resAsync = parseUserInput(userInput)
.andTee(logUser)
.asyncAndThen(insertUser)
// Note no LogError shows up in the Result type
resAsync.then((res: Result<void, ParseError | InsertError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User input has been parsed and inserted successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `Result.orTee` (method)
Like `andTee` for the error track. Takes a `Result<T, E>` and lets the `Err` value pass through regardless the result of the passed-in function.
This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
**Signature:**
```typescript
class Result<T, E> {
orTee(
callback: (value: E) => unknown
): Result<T, E> { ... }
}
```
**Example:**
```typescript
import { parseUserInput } from 'imaginary-parser'
import { logParseError } from 'imaginary-logger'
import { insertUser } from 'imaginary-database'
// ^ assume parseUserInput, logParseError and insertUser have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// logParseError(parseError: ParseError): Result<void, LogError>
// insertUser(user: User): ResultAsync<void, InsertError>
// Note logParseError returns void upon success but insertUser takes User type.
const resAsync = parseUserInput(userInput)
.orTee(logParseError)
.asyncAndThen(insertUser)
// Note no LogError shows up in the Result type
resAsync.then((res: Result<void, ParseError | InsertError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User input has been parsed and inserted successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `Result.andThrough` (method)
Similar to `andTee` except for:
- when there is an error from the passed-in function, that error will be passed along.
**Signature:**
```typescript
class Result<T, E> {
andThrough<F>(
callback: (value: T) => Result<unknown, F>
): Result<T, E | F> { ... }
}
```
**Example:**
```typescript
import { parseUserInput } from 'imaginary-parser'
import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
// ^ assume parseUseInput, validateUser and insertUser have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// validateUser(user: User): Result<void, ValidateError>
// insertUser(user: User): ResultAsync<void, InsertError>
// Note validateUser returns void upon success but insertUser takes User type.
const resAsync = parseUserInput(userInput)
.andThrough(validateUser)
.asyncAndThen(insertUser)
resAsync.then((res: Result<void, ParseError | ValidateError | InsertError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User input has been parsed, validated, inserted successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `Result.asyncAndThrough` (method)
Similar to `andThrough` except you must return a ResultAsync.
You can then chain the result of `asyncAndThrough` using the `ResultAsync` apis (like `map`, `mapErr`, `andThen`, etc.)
**Signature:**
```typescript
import { parseUserInput } from 'imaginary-parser'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'
// ^ assume parseUserInput, insertUser and sendNotification have the following signatures:
// parseUserInput(input: RequestData): Result<User, ParseError>
// insertUser(user: User): ResultAsync<void, InsertError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note insertUser returns void upon success but sendNotification takes User type.
const resAsync = parseUserInput(userInput)
.asyncAndThrough(insertUser)
.andThen(sendNotification)
resAsync.then((res: Result<void, ParseError | InsertError | NotificationError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User has been parsed, inserted and notified successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `Result.fromThrowable` (static class method)
> Although Result is not an actual JS class, the way that `fromThrowable` has been implemented requires that you call `fromThrowable` as though it were a static method on `Result`. See examples below.
The JavaScript community has agreed on the convention of throwing exceptions.
As such, when interfacing with third party libraries it's imperative that you
wrap third-party code in try / catch blocks.
This function will create a new function that returns an `Err` when the original
function throws.
It is not possible to know the types of the errors thrown in the original
function, therefore it is recommended to use the second argument `errorFn` to
map what is thrown to a known type.
**Example**:
```typescript
import { Result } from 'neverthrow'
type ParseError = { message: string }
const toParseError = (): ParseError => ({ message: "Parse Error" })
const safeJsonParse = Result.fromThrowable(JSON.parse, toParseError)
// the function can now be used safely, if the function throws, the result will be an Err
const res = safeJsonParse("{");
```
[⬆️ Back to top](#toc)
---
#### `Result.combine` (static class method)
> Although Result is not an actual JS class, the way that `combine` has been implemented requires that you call `combine` as though it were a static method on `Result`. See examples below.
Combine lists of `Result`s.
If you're familiar with `Promise.all`, the combine function works conceptually the same.
**`combine` works on both heterogeneous and homogeneous lists**. This means that you can have lists that contain different kinds of `Result`s and still be able to combine them. Note that you cannot combine lists that contain both `Result`s **and** `ResultAsync`s.
The combine function takes a list of results and returns 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
// homogeneous lists
function combine<T, E>(resultList: Result<T, E>[]): Result<T[], E>
// heterogeneous lists
function combine<T1, T2, E1, E2>(resultList: [ Result<T1, E1>, Result<T2, E2> ]): Result<[ T1, T2 ], E1 | E2>
function combine<T1, T2, T3, E1, E2, E3> => Result<[ T1, T2, T3 ], E1 | E2 | E3>
function combine<T1, T2, T3, T4, E1, E2, E3, E4> => Result<[ T1, T2, T3, T4 ], E1 | E2 | E3 | E4>
// ... etc etc ad infinitum
```
Example:
```typescript
const resultList: Result<number, never>[] =
[ok(1), ok(2)]
const combinedList: Result<number[], unknown> =
Result.combine(resultList)
```
Example with tuples:
```typescript
/** @example tuple(1, 2, 3) === [1, 2, 3] // with type [number, number, number] */
const tuple = <T extends any[]>(...args: T): T => args
const resultTuple: [Result<string, never>, Result<string, never>] =
tuple(ok('a'), ok('b'))
const combinedTuple: Result<[string, string], unknown> =
Result.combine(resultTuple)
```
[⬆️ Back to top](#toc)
---
#### `Result.combineWithAllErrors` (static class method)
> Although Result is not an actual JS class, the way that `combineWithAllErrors` has been implemented requires that you call `combineWithAllErrors` as though it were a static method on `Result`. See examples below.
Like `combine` but without short-circuiting. Instead of just the first error value, you get a list of all error values of the input result list.
If only some results fail, the new combined error list will only contain the error value of the failed results, meaning that there is no guarantee of the length of the new error list.
Function signature:
```typescript
// homogeneous lists
function combineWithAllErrors<T, E>(resultList: Result<T, E>[]): Result<T[], E[]>
// heterogeneous lists
function combineWithAllErrors<T1, T2, E1, E2>(resultList: [ Result<T1, E1>, Result<T2, E2> ]): Result<[ T1, T2 ], (E1 | E2)[]>
function combineWithAllErrors<T1, T2, T3, E1, E2, E3> => Result<[ T1, T2, T3 ], (E1 | E2 | E3)[]>
function combineWithAllErrors<T1, T2, T3, T4, E1, E2, E3, E4> => Result<[ T1, T2, T3, T4 ], (E1 | E2 | E3 | E4)[]>
// ... etc etc ad infinitum
```
Example usage:
```typescript
const resultList: Result<number, string>[] = [
ok(123),
err('boooom!'),
ok(456),
err('ahhhhh!'),
]
const result = Result.combineWithAllErrors(resultList)
// result is Err(['boooom!', 'ahhhhh!'])
```
[⬆️ Back to top](#toc)
#### `Result.safeUnwrap()`
**Deprecated**. You don't need to use this method anymore.
Allows for unwrapping a `Result` or returning an `Err` implicitly, thereby reducing boilerplate.
[⬆️ Back to top](#toc)
---
### Asynchronous API (`ResultAsync`)
#### `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
```
[⬆️ Back to top](#toc)
---
#### `errAsync`
Constructs an `Err` variant of `ResultAsync`
**Signature:**
```typescript
errAsync<T, E>(error: 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
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.fromThrowable` (static class method)
Similar to [Result.fromThrowable](#resultfromthrowable-static-class-method), but for functions that return a `Promise`.
**Example**:
```typescript
import { ResultAsync } from 'neverthrow'
import { insertIntoDb } from 'imaginary-database'
// insertIntoDb(user: User): Promise<User>
const insertUser = ResultAsync.fromThrowable(insertIntoDb, () => new Error('Database error'))
// `res` has a type of (user: User) => ResultAsync<User, Error>
```
Note that this can be safer than using [ResultAsync.fromPromise](#resultasyncfrompromise-static-class-method) with
the result of a function call, because not all functions that return a `Promise` are `async`, and thus they can throw
errors synchronously rather than returning a rejected `Promise`. For example:
```typescript
// NOT SAFE !!
import { ResultAsync } from 'neverthrow'
import { db } from 'imaginary-database'
// db.insert<T>(table: string, value: T): Promise<T>
const insertUser = (user: User): Promise<User> => {
if (!user.id) {
// this throws synchronously!
throw new TypeError('missing user id')
}
return db.insert('users', user)
}
// this will throw, NOT return a `ResultAsync`
const res = ResultAsync.fromPromise(insertIntoDb(myUser), () => new Error('Database error'))
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.fromPromise` (static class method)
Transforms a `PromiseLike<T>` (that may throw) into a `ResultAsync<T, E>`.
The second argument handles the rejection case of the promise and maps the error from `unknown` into some type `E`.
**Signature:**
```typescript
// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync.fromPromise<T, E>(
promise: PromiseLike<T>,
errorHandler: (unknownError: unknown) => E)
): ResultAsync<T, E> { ... }
```
If you are working with `PromiseLike` objects that you **know for a fact** will not throw, then use `fromSafePromise` in order to avoid having to pass a redundant `errorHandler` argument.
**Example**:
```typescript
import { ResultAsync } from 'neverthrow'
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>
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.fromSafePromise` (static class method)
Same as `ResultAsync.fromPromise` except that it does not handle the rejection of the promise. **Ensure you know what you're doing, otherwise a thrown exception within this promise will cause ResultAsync to reject, instead of resolve to a Result.**
**Signature:**
```typescript
// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync.fromSafePromise<T, E>(
promise: PromiseLike<T>
): ResultAsync<T, E> { ... }
```
**Example**:
```typescript
import { RouteError } from 'routes/error'
// simulate slow routes in an http server that works in a Result / ResultAsync context
// Adopted from https://github.com/parlez-vous/server/blob/2496bacf55a2acbebc30631b5562f34272794d76/src/routes/common/signup.ts
export const slowDown = <T>(ms: number) => (value: T) =>
ResultAsync.fromSafePromise<T, RouteError>(
new Promise((resolve) => {
setTimeout(() => {
resolve(value)
}, ms)
})
)
export const signupHandler = route<User>((req, sessionManager) =>
decode(userSignupDecoder, req.body, 'Invalid request body').map((parsed) => {
return createUser(parsed)
.andThen(slowDown(3000)) // slowdown by 3 seconds
.andThen(sessionManager.createSession)
.map(({ sessionToken, admin }) => AppData.init(admin, sessionToken))
})
)
```
[⬆️ Back to top](#toc)
---
#### `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
class ResultAsync<T, E> {
map<U>(
callback: (value: T) => U | Promise<U>
): ResultAsync<U, E> { ... }
}
```
**Example**:
```typescript
import { 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(','))
}
})
```
[⬆️ Back to top](#toc)
---
#### `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
class ResultAsync<T, E> {
mapErr<F>(
callback: (error: E) => F | Promise<F>
): ResultAsync<T, F> { ... }
}
```
**Example**:
```typescript
import { 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((error: Error) => {
// The only error we want to pass to the user is "Unknown country"
if(error.message === "Unknown country"){
return error.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
})
}
})
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.unwrapOr` (method)
Unwrap the `Ok` value, or return the default if there is an `Err`.
Works just like `Result.unwrapOr` but returns a `Promise<T>` instead of `T`.
**Signature:**
```typescript
class ResultAsync<T, E> {
unwrapOr<T>(value: T): Promise<T> { ... }
}
```
**Example**:
```typescript
const unwrapped: number = await errAsync(0).unwrapOr(10)
// unwrapped = 10
```
[⬆️ Back to top](#toc)
---
#### `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
// Note that the latest version (v4.1.0-beta) lets you return distinct errors as well.
// If the error types (E and F) are the same (like `string | string`)
// then they will be merged into one type (`string`)
class ResultAsync<T, E> {
andThen<U, F>(
callback: (value: T) => Result<U, F> | ResultAsync<U, F>
): ResultAsync<U, E | F> { ... }
}
```
**Example**
```typescript
import { validateUser } from 'imaginary-validator'
import { insertUser } from 'imaginary-database'
import { 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.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.orElse` (method)
Takes an `Err` value and maps it to a `ResultAsync<T, SomeNewType>`. This is useful for error recovery.
**Signature:**
```typescript
class ResultAsync<T, E> {
orElse<U, A>(
callback: (error: E) => Result<U, A> | ResultAsync<U, A>
): ResultAsync<U | T, A> { ... }
}
```
[⬆️ Back to top](#toc)
---
#### `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
class ResultAsync<T, E> {
match<A, B = A>(
okCallback: (value: T) => A,
errorCallback: (error: E) => B
): Promise<A | B> => { ... }
}
```
**Example:**
```typescript
import { validateUser } from 'imaginary-validator'
import { 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`,
(error: Error) => `User could not be created because ${error.message}`
)
// resultMessage is a string
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.andTee` (method)
Takes a `ResultAsync<T, E>` and lets the original `ResultAsync<T, E>` pass through regardless
the result of the passed-in function.
This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
**Signature:**
```typescript
class ResultAsync<T, E> {
andTee(
callback: (value: T) => unknown
): ResultAsync<T, E> => { ... }
}
```
**Example:**
```typescript
import { insertUser } from 'imaginary-database'
import { logUser } from 'imaginary-logger'
import { sendNotification } from 'imaginary-service'
// ^ assume insertUser, logUser and sendNotification have the following signatures:
// insertUser(user: User): ResultAsync<User, InsertError>
// logUser(user: User): Result<void, LogError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note logUser returns void on success but sendNotification takes User type.
const resAsync = insertUser(user)
.andTee(logUser)
.andThen(sendNotification)
// Note there is no LogError in the types below
resAsync.then((res: Result<void, InsertError | NotificationError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User has been inserted and notified successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.orTee` (method)
Like `andTee` for the error track. Takes a `ResultAsync<T, E>` and lets the original `Err` value pass through regardless
the result of the passed-in function.
This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
**Signature:**
```typescript
class ResultAsync<T, E> {
orTee(
callback: (value: E) => unknown
): ResultAsync<T, E> => { ... }
}
```
**Example:**
```typescript
import { insertUser } from 'imaginary-database'
import { logInsertError } from 'imaginary-logger'
import { sendNotification } from 'imaginary-service'
// ^ assume insertUser, logInsertError and sendNotification have the following signatures:
// insertUser(user: User): ResultAsync<User, InsertError>
// logInsertError(insertError: InsertError): Result<void, LogError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note logInsertError returns void on success but sendNotification takes User type.
const resAsync = insertUser(user)
.orTee(logInsertError)
.andThen(sendNotification)
// Note there is no LogError in the types below
resAsync.then((res: Result<void, InsertError | NotificationError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User has been inserted and notified successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.andThrough` (method)
Similar to `andTee` except for:
- when there is an error from the passed-in function, that error will be passed along.
**Signature:**
```typescript
class ResultAsync<T, E> {
andThrough<F>(
callback: (value: T) => Result<unknown, F> | ResultAsync<unknown, F>,
): ResultAsync<T, E | F> => { ... }
}
```
**Example:**
```typescript
import { buildUser } from 'imaginary-builder'
import { insertUser } from 'imaginary-database'
import { sendNotification } from 'imaginary-service'
// ^ assume buildUser, insertUser and sendNotification have the following signatures:
// buildUser(userRaw: UserRaw): ResultAsync<User, BuildError>
// insertUser(user: User): ResultAsync<void, InsertError>
// sendNotification(user: User): ResultAsync<void, NotificationError>
// Note insertUser returns void upon success but sendNotification takes User type.
const resAsync = buildUser(userRaw)
.andThrough(insertUser)
.andThen(sendNotification)
resAsync.then((res: Result<void, BuildError | InsertError | NotificationError>) => {
if(res.isErr()){
console.log("Oops, at least one step failed", res.error)
}
else{
console.log("User data has been built, inserted and notified successfully.")
}
})
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.combine` (static class method)
Combine lists of `ResultAsync`s.
If you're familiar with `Promise.all`, the combine function works conceptually the same.
**`combine` works on both heterogeneous and homogeneous lists**. This means that you can have lists that contain different kinds of `ResultAsync`s and still be able to combine them. Note that you cannot combine lists that contain both `Result`s **and** `ResultAsync`s.
The combine function takes a list of results and returns 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
// homogeneous lists
function combine<T, E>(resultList: ResultAsync<T, E>[]): ResultAsync<T[], E>
// heterogeneous lists
function combine<T1, T2, E1, E2>(resultList: [ ResultAsync<T1, E1>, ResultAsync<T2, E2> ]): ResultAsync<[ T1, T2 ], E1 | E2>
function combine<T1, T2, T3, E1, E2, E3> => ResultAsync<[ T1, T2, T3 ], E1 | E2 | E3>
function combine<T1, T2, T3, T4, E1, E2, E3, E4> => ResultAsync<[ T1, T2, T3, T4 ], E1 | E2 | E3 | E4>
// ... etc etc ad infinitum
```
Example:
```typescript
const resultList: ResultAsync<number, never>[] =
[okAsync(1), okAsync(2)]
const combinedList: ResultAsync<number[], unknown> =
ResultAsync.combine(resultList)
```
Example with tuples:
```typescript
/** @example tuple(1, 2, 3) === [1, 2, 3] // with type [number, number, number] */
const tuple = <T extends any[]>(...args: T): T => args
const resultTuple: [ResultAsync<string, never>, ResultAsync<string, never>] =
tuple(okAsync('a'), okAsync('b'))
const combinedTuple: ResultAsync<[string, string], unknown> =
ResultAsync.combine(resultTuple)
```
[⬆️ Back to top](#toc)
---
#### `ResultAsync.combineWithAllErrors` (static class method)
Like `combine` but without short-circuiting. Instead of just the first error value, you get a list of all error values of the input result list.
If only some results fail, the new combined error list will only contain the error value of the failed results, meaning that there is no guarantee of the length of the new error list.
Function signature:
```typescript
// homogeneous lists
function combineWithAllErrors<T, E>(resultList: ResultAsync<T, E>[]): ResultAsync<T[], E[]>
// heterogeneous lists
function combineWithAllErrors<T1, T2, E1, E2>(resultList: [ ResultAsync<T1, E1>, ResultAsync<T2, E2> ]): ResultAsync<[ T1, T2 ], (E1 | E2)[]>
function combineWithAllErrors<T1, T2, T3, E1, E2, E3> => ResultAsync<[ T1, T2, T3 ], (E1 | E2 | E3)[]>
function combineWithAllErrors<T1, T2, T3, T4, E1, E2, E3, E4> => ResultAsync<[ T1, T2, T3, T4 ], (E1 | E2 | E3 | E4)[]>
// ... etc etc ad infinitum
```
Example usage:
```typescript
const resultList: ResultAsync<number, string>[] = [
okAsync(123),
errAsync('boooom!'),
okAsync(456),
errAsync('ahhhhh!'),
]
const result = ResultAsync.combineWithAllErrors(resultList)
// result is Err(['boooom!', 'ahhhhh!'])
```
#### `ResultAsync.safeUnwrap()`
**Deprecated**. You don't need to use this method anymore.
Allows for unwrapping a `Result` or returning an `Err` implicitly, thereby reducing boilerplate.
[⬆️ Back to top](#toc)
---
### Utilities
#### `fromThrowable`
Top level export of `Result.fromThrowable`.
Please find documentation at [Result.fromThrowable](#resultfromthrowable-static-class-method)
[⬆️ Back to top](#toc)
#### `fromAsyncThrowable`
Top level export of `ResultAsync.fromThrowable`.
Please find documentation at [ResultAsync.fromThrowable](#resultasyncfromthrowable-static-class-method)
[⬆️ Back to top](#toc)
#### `fromPromise`
Top level export of `ResultAsync.fromPromise`.
Please find documentation at [ResultAsync.fromPromise](#resultasyncfrompromise-static-class-method)
[⬆️ Back to top](#toc)
#### `fromSafePromise`
Top level export of `ResultAsync.fromSafePromise`.
Please find documentation at [ResultAsync.fromSafePromise](#resultasyncfromsafepromise-static-class-method)
[⬆️ Back to top](#toc)
#### `safeTry`
Used to implicitly return errors and reduce boilerplate.
Let's say we are writing a function that returns a `Result`, and in that function we call some functions which also return `Result`s and we check those results to see whether we should keep going or abort. Usually, we will write like the following.
```typescript
declare function mayFail1(): Result<number, string>;
declare function mayFail2(): Result<number, string>;
function myFunc(): Result<number, string> {
// We have to define a constant to hold the result to check and unwrap its value.
const result1 = mayFail1();
if (result1.isErr()) {
return err(`aborted by an error from 1st function, ${result1.error}`);
}
const value1 = result1.value
// Again, we need to define a constant and then check and unwrap.
const result2 = mayFail2();
if (result2.isErr()) {
return err(`aborted by an error from 2nd function, ${result2.error}`);
}
const value2 = result2.value
// And finally we return what we want to calculate
return ok(value1 + value2);
}
```
Basically, we need to define a constant for each result to check whether it's a `Ok` and read its `.value` or `.error`.
With safeTry, we can state 'Return here if its an `Err`, otherwise unwrap it here and keep going.' in just one expression.
```typescript
declare function mayFail1(): Result<number, string>;
declare function mayFail2(): Result<number, string>;
function myFunc(): Result<number, string> {
return safeTry<number, string>(function*() {
return ok(
// If the result of mayFail1().mapErr() is an `Err`, the evaluation is
// aborted here and the enclosing `safeTry` block is evaluated to that `Err`.
// Otherwise, this `(yield* ...)` is evaluated to its `.value`.
(yield* mayFail1()
.mapErr(e => `aborted by an error from 1st function, ${e}`))
+
// The same as above.
(yield* mayFail2()
.mapErr(e => `aborted by an error from 2nd function, ${e}`))
)
})
}
```
To use `safeTry`, the points are as follows.
* Wrap the entire block in a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
* In that block, you can use `yield* <RESULT>` to state 'Return `<RESULT>` if it's an `Err`, otherwise evaluate to its `.value`'
* Pass the generator function to `safeTry`
You can also use [async generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*) to pass an async block to `safeTry`.
```typescript
// You can use either Promise<Result> or ResultAsync.
declare function mayFail1(): Promise<Result<number, string>>;
declare function mayFail2(): ResultAsync<number, string>;
function myFunc(): Promise<Result<number, string>> {
return safeTry<number, string>(async function*() {
return ok(
// You have to await if the expression is Promise<Result>
(yield* (await mayFail1())
.mapErr(e => `aborted by an error from 1st function, ${e}`))
+
// You can call `safeUnwrap` directly if its ResultAsync
(yield* mayFail2()
.mapErr(e => `aborted by an error from 2nd function, ${e}`))
)
})
}
```
For more information, see https://github.com/supermacro/neverthrow/pull/448 and https://github.com/supermacro/neverthrow/issues/444
[⬆️ Back to top](#toc)
---
### Testing
`Result` instances have two unsafe methods, aptly called `_unsafeUnwrap` and `_unsafeUnwrapErr` which **should only be used in a test environment**.
`_unsafeUnwrap` takes a `Result<T, E>` and returns a `T` when the result is an `Ok`, otherwise it throws a custom object.
`_unsafeUnwrapErr` takes a `Result<T, E>` and returns a `E` when the result is an `Err`, otherwise it throws a custom object.
That way you can do something like:
```typescript
expect(myResult._unsafeUnwrap()).toBe(someExpectation)
```
However, do note that `Result` instances are comparable. So you don't necessarily need to unwrap them in order to assert expectations in your tests. So you could also do something like this:
```typescript
import { ok } from 'neverthrow'
// ...
expect(callSomeFunctionThatReturnsAResult("with", "some", "args")).toEqual(ok(someExpectation));
```
By default, the thrown value does not contain a stack trace. This is because stack trace generation [makes error messages in Jest harder to understand](https://github.com/supermacro/neverthrow/pull/215). If you want stack traces to be generated, call `_unsafeUnwrap` and / or `_unsafeUnwrapErr` with a config object:
```typescript
_unsafeUnwrapErr({
withStackTrace: true,
})
// ^ Now the error object will have a `.stack` property containing the current stack
```
---
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
Although the package is called `neverthrow`, please don't take this literally. I am simply encouraging the developer to think a bit more about the ergonomics and usage of whatever software they are writing.
`Throw`ing and `catching` is very similar to using `goto` statements - in other words; it makes reasoning about your programs harder. Secondly, by using `throw` you make the assumption that the caller of your function is implementing `catch`. This is a known source of errors. Example: One dev `throw`s and another dev uses the function without prior knowledge that the function will throw. Thus, and edge case has been left unhandled and now you have unhappy users, bosses, cats, etc.
With all that said, there are definitely good use cases for throwing in your program. But much less than you might think.
### License
The neverthrow project is available as open source under the terms of the [MIT license](https://github.com/supermacro/neverthrow/blob/master/LICENSE).
================================================
FILE: jest.config.js
================================================
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
================================================
FILE: package.json
================================================
{
"name": "neverthrow",
"version": "8.2.0",
"description": "Stop throwing errors, and instead return Results!",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"local-ci": "npm run typecheck && npm run lint && npm run test && npm run format && npm run build",
"test": "vitest run && npm run test-types",
"test-types": "tsc --noEmit -p ./tests/tsconfig.tests.json",
"lint": "eslint ./src --ext .ts",
"format": "prettier --write 'src/**/*.ts?(x)' && npm run lint -- --fix",
"typecheck": "tsc --noEmit",
"clean": "rm -rf ./dist ./tmp",
"build": "npm run clean && rollup --config && mv tmp/*.js dist && attw --pack .",
"prepublishOnly": "npm run build",
"release": "changeset publish",
"version": "changeset version && npm i --lockfile-only"
},
"repository": {
"type": "git",
"url": "git+https://github.com/supermacro/neverthrow.git"
},
"author": "Giorgio Delgado",
"license": "MIT",
"bugs": {
"url": "https://github.com/supermacro/neverthrow/issues"
},
"homepage": "https://github.com/supermacro/neverthrow#readme",
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.3",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.7",
"@types/node": "^18.19.39",
"@typescript-eslint/eslint-plugin": "4.28.1",
"@typescript-eslint/parser": "4.28.1",
"eslint": "7.30.0",
"eslint-config-prettier": "7.1.0",
"eslint-plugin-prettier": "3.4.0",
"prettier": "2.2.1",
"rollup": "^4.18.0",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-typescript2": "^0.32.1",
"testdouble": "3.20.2",
"ts-toolbelt": "9.6.0",
"typescript": "4.7.2",
"vitest": "^2.1.3"
},
"keywords": [
"typescript",
"functional",
"fp",
"error"
],
"engines": {
"node": ">=18",
"npm": ">=11"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.24.0"
}
}
================================================
FILE: rollup.config.mjs
================================================
import typescript from "rollup-plugin-typescript2";
import dts from "rollup-plugin-dts";
/**
* Just a few details about this build implementation:
* - Uses `rollup` along with `rollup-plugin-typescript2` to build to two target files
* (`index.es.js` and `index.cjs.js`). This means that there is only a *single* file now for each
* target rather than 4 files (ie. `index.js`, `chain.js`, etc.).
* - Rollup has their own official @rollup/plugin-typescript plugin but it does not have the best
* TS declarations support atm (see https://github.com/rollup/plugins/issues/394,
* https://github.com/rollup/plugins/issues/254, https://github.com/rollup/plugins/issues/243).
* Until these are resolved, I choose to just use `rollup-plugin-typescript2`.
* - `rollup-plugin-typescript2` generates `index.d.ts`, `chain.d.ts`, `result.d.ts` and
* `result-async.d.ts` but this is inconsistent with the generated JavaScript files. This isn't a
* huge issues unless someone tries to import `neverthrow/dist/chain` which would error
* because the underlying `.js` doesn't exist. To remedy this issue, I used `rollup-plugin-dts` to
* merge `*.d.ts` files into a single `index.d.ts`.
* - It's unfortunately a bit complicated to generate two build outputs but once some of the
* issues I've linked to above are resolved this process will become much easier :)
* - Because the build process is kinda two steps (first using `rollup-plugin-typescript2` and then
* using `rollup-plugin-dts`), I first write to `tmp/`, then write the merged `index.d.ts` to
* `dist/` and then moved `index.es.js` and `index.cjs.js` to `dist/`.
*/
export default [
{
input: "src/index.ts",
output: {
file: "tmp/index.es.js",
format: "es",
},
plugins: [typescript()],
},
{
input: "src/index.ts",
output: {
file: "tmp/index.cjs.js",
format: "cjs",
},
plugins: [typescript()],
},
{
input: "tmp/index.d.ts",
output: [{ file: "dist/index.d.ts", format: "es" }],
plugins: [dts()],
},
];
================================================
FILE: src/_internals/error.ts
================================================
import { Result } from '../result'
export interface ErrorConfig {
withStackTrace: boolean
}
const defaultErrorConfig: ErrorConfig = {
withStackTrace: false,
}
interface NeverThrowError<T, E> {
data:
| {
type: string
value: T
}
| {
type: string
value: E
}
message: string
stack: string | undefined
}
// Custom error object
// Context / discussion: https://github.com/supermacro/neverthrow/pull/215
export const createNeverThrowError = <T, E>(
message: string,
result: Result<T, E>,
config: ErrorConfig = defaultErrorConfig,
): NeverThrowError<T, E> => {
const data = result.isOk()
? { type: 'Ok', value: result.value }
: { type: 'Err', value: result.error }
const maybeStack = config.withStackTrace ? new Error().stack : undefined
return {
data,
message,
stack: maybeStack,
}
}
================================================
FILE: src/_internals/utils.ts
================================================
import { Result, ok, err } from '../result'
import { ResultAsync } from '../result-async'
// Given a list of Results, this extracts all the different `T` types from that list
export type ExtractOkTypes<T extends readonly Result<unknown, unknown>[]> = {
[idx in keyof T]: T[idx] extends Result<infer U, unknown> ? U : never
}
// Given a list of ResultAsyncs, this extracts all the different `T` types from that list
export type ExtractOkAsyncTypes<T extends readonly ResultAsync<unknown, unknown>[]> = {
[idx in keyof T]: T[idx] extends ResultAsync<infer U, unknown> ? U : never
}
// Given a list of Results, this extracts all the different `E` types from that list
export type ExtractErrTypes<T extends readonly Result<unknown, unknown>[]> = {
[idx in keyof T]: T[idx] extends Result<unknown, infer E> ? E : never
}
// Given a list of ResultAsyncs, this extracts all the different `E` types from that list
export type ExtractErrAsyncTypes<T extends readonly ResultAsync<unknown, unknown>[]> = {
[idx in keyof T]: T[idx] extends ResultAsync<unknown, infer E> ? E : never
}
export type InferOkTypes<R> = R extends Result<infer T, unknown> ? T : never
export type InferErrTypes<R> = R extends Result<unknown, infer E> ? E : never
export type InferAsyncOkTypes<R> = R extends ResultAsync<infer T, unknown> ? T : never
export type InferAsyncErrTypes<R> = R extends ResultAsync<unknown, infer E> ? E : never
/**
* Short circuits on the FIRST Err value that we find
*/
export const combineResultList = <T, E>(
resultList: readonly Result<T, E>[],
): Result<readonly T[], E> => {
let acc = ok([]) as Result<T[], E>
for (const result of resultList) {
if (result.isErr()) {
acc = err(result.error)
break
} else {
acc.map((list) => list.push(result.value))
}
}
return acc
}
/* This is the typesafe version of Promise.all
*
* Takes a list of ResultAsync<T, E> and success if all inner results are Ok values
* or fails if one (or more) of the inner results are Err values
*/
export const combineResultAsyncList = <T, E>(
asyncResultList: readonly ResultAsync<T, E>[],
): ResultAsync<readonly T[], E> =>
ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(
combineResultList,
) as ResultAsync<T[], E>
/**
* Give a list of all the errors we find
*/
export const combineResultListWithAllErrors = <T, E>(
resultList: readonly Result<T, E>[],
): Result<readonly T[], E[]> => {
let acc = ok([]) as Result<T[], E[]>
for (const result of resultList) {
if (result.isErr() && acc.isErr()) {
acc.error.push(result.error)
} else if (result.isErr() && acc.isOk()) {
acc = err([result.error])
} else if (result.isOk() && acc.isOk()) {
acc.value.push(result.value)
}
// do nothing when result.isOk() && acc.isErr()
}
return acc
}
export const combineResultAsyncListWithAllErrors = <T, E>(
asyncResultList: readonly ResultAsync<T, E>[],
): ResultAsync<readonly T[], E[]> =>
ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(
combineResultListWithAllErrors,
) as ResultAsync<T[], E[]>
================================================
FILE: src/index.ts
================================================
export { Result, ok, Ok, err, Err, fromThrowable, safeTry } from './result'
export {
ResultAsync,
okAsync,
errAsync,
fromAsyncThrowable,
fromPromise,
fromSafePromise,
} from './result-async'
================================================
FILE: src/result-async.ts
================================================
import type {
Combine,
Dedup,
EmptyArrayToNever,
IsLiteralArray,
MemberListOf,
MembersToUnion,
} from './result'
import { Err, Ok, Result } from './'
import {
combineResultAsyncList,
combineResultAsyncListWithAllErrors,
ExtractErrAsyncTypes,
ExtractOkAsyncTypes,
InferAsyncErrTypes,
InferAsyncOkTypes,
InferErrTypes,
InferOkTypes,
} from './_internals/utils'
export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
private _promise: Promise<Result<T, E>>
constructor(res: Promise<Result<T, E>>) {
this._promise = res
}
static fromSafePromise<T, E = never>(promise: PromiseLike<T>): ResultAsync<T, E>
static fromSafePromise<T, E = never>(promise: Promise<T>): ResultAsync<T, E> {
const newPromise = promise.then((value: T) => new Ok<T, E>(value))
return new ResultAsync(newPromise)
}
static fromPromise<T, E>(promise: PromiseLike<T>, errorFn: (e: unknown) => E): ResultAsync<T, E>
static fromPromise<T, E>(promise: Promise<T>, errorFn: (e: unknown) => E): ResultAsync<T, E> {
const newPromise = promise
.then((value: T) => new Ok<T, E>(value))
.catch((e) => new Err<T, E>(errorFn(e)))
return new ResultAsync(newPromise)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static fromThrowable<A extends readonly any[], R, E>(
fn: (...args: A) => Promise<R>,
errorFn?: (err: unknown) => E,
): (...args: A) => ResultAsync<R, E> {
return (...args) => {
return new ResultAsync(
(async () => {
try {
return new Ok(await fn(...args))
} catch (error) {
return new Err(errorFn ? errorFn(error) : error)
}
})(),
)
}
}
static combine<
T extends readonly [ResultAsync<unknown, unknown>, ...ResultAsync<unknown, unknown>[]]
>(asyncResultList: T): CombineResultAsyncs<T>
static combine<T extends readonly ResultAsync<unknown, unknown>[]>(
asyncResultList: T,
): CombineResultAsyncs<T>
static combine<T extends readonly ResultAsync<unknown, unknown>[]>(
asyncResultList: T,
): CombineResultAsyncs<T> {
return (combineResultAsyncList(asyncResultList) as unknown) as CombineResultAsyncs<T>
}
static combineWithAllErrors<
T extends readonly [ResultAsync<unknown, unknown>, ...ResultAsync<unknown, unknown>[]]
>(asyncResultList: T): CombineResultsWithAllErrorsArrayAsync<T>
static combineWithAllErrors<T extends readonly ResultAsync<unknown, unknown>[]>(
asyncResultList: T,
): CombineResultsWithAllErrorsArrayAsync<T>
static combineWithAllErrors<T extends readonly ResultAsync<unknown, unknown>[]>(
asyncResultList: T,
): CombineResultsWithAllErrorsArrayAsync<T> {
return combineResultAsyncListWithAllErrors(
asyncResultList,
) as CombineResultsWithAllErrorsArrayAsync<T>
}
map<A>(f: (t: T) => A | Promise<A>): ResultAsync<A, E> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isErr()) {
return new Err<A, E>(res.error)
}
return new Ok<A, E>(await f(res.value))
}),
)
}
andThrough<F>(f: (t: T) => Result<unknown, F> | ResultAsync<unknown, F>): ResultAsync<T, E | F> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isErr()) {
return new Err<T, E>(res.error)
}
const newRes = await f(res.value)
if (newRes.isErr()) {
return new Err<T, F>(newRes.error)
}
return new Ok<T, F>(res.value)
}),
)
}
andTee(f: (t: T) => unknown): ResultAsync<T, E> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isErr()) {
return new Err<T, E>(res.error)
}
try {
await f(res.value)
} catch (e) {
// Tee does not care about the error
}
return new Ok<T, E>(res.value)
}),
)
}
orTee(f: (t: E) => unknown): ResultAsync<T, E> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isOk()) {
return new Ok<T, E>(res.value)
}
try {
await f(res.error)
} catch (e) {
// Tee does not care about the error
}
return new Err<T, E>(res.error)
}),
)
}
mapErr<U>(f: (e: E) => U | Promise<U>): ResultAsync<T, U> {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isOk()) {
return new Ok<T, U>(res.value)
}
return new Err<T, U>(await f(res.error))
}),
)
}
andThen<R extends Result<unknown, unknown>>(
f: (t: T) => R,
): ResultAsync<InferOkTypes<R>, InferErrTypes<R> | E>
andThen<R extends ResultAsync<unknown, unknown>>(
f: (t: T) => R,
): ResultAsync<InferAsyncOkTypes<R>, InferAsyncErrTypes<R> | E>
andThen<U, F>(f: (t: T) => Result<U, F> | ResultAsync<U, F>): ResultAsync<U, E | F>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
andThen(f: any): any {
return new ResultAsync(
this._promise.then((res) => {
if (res.isErr()) {
return new Err<never, E>(res.error)
}
const newValue = f(res.value)
return newValue instanceof ResultAsync ? newValue._promise : newValue
}),
)
}
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<R extends ResultAsync<unknown, unknown>>(
f: (e: E) => R,
): ResultAsync<InferAsyncOkTypes<R> | T, InferAsyncErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A> | ResultAsync<U, A>): ResultAsync<U | T, A>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
orElse(f: any): any {
return new ResultAsync(
this._promise.then(async (res: Result<T, E>) => {
if (res.isErr()) {
return f(res.error)
}
return new Ok<T, unknown>(res.value)
}),
)
}
match<A, B = A>(ok: (t: T) => A, _err: (e: E) => B): Promise<A | B> {
return this._promise.then((res) => res.match(ok, _err))
}
unwrapOr<A>(t: A): Promise<T | A> {
return this._promise.then((res) => res.unwrapOr(t))
}
/**
* @deprecated will be removed in 9.0.0.
*
* You can use `safeTry` without this method.
* @example
* ```typescript
* safeTry(async function* () {
* const okValue = yield* yourResult
* })
* ```
* Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
*/
async *safeUnwrap(): AsyncGenerator<Err<never, E>, T> {
return yield* await this._promise.then((res) => res.safeUnwrap())
}
// Makes ResultAsync implement PromiseLike<Result>
then<A, B>(
successCallback?: (res: Result<T, E>) => A | PromiseLike<A>,
failureCallback?: (reason: unknown) => B | PromiseLike<B>,
): PromiseLike<A | B> {
return this._promise.then(successCallback, failureCallback)
}
async *[Symbol.asyncIterator](): AsyncGenerator<Err<never, E>, T> {
const result = await this._promise
if (result.isErr()) {
// @ts-expect-error -- This is structurally equivalent and safe
yield errAsync(result.error)
}
// @ts-expect-error -- This is structurally equivalent and safe
return result.value
}
}
export function okAsync<T, E = never>(value: T): ResultAsync<T, E>
export function okAsync<T extends void = void, E = never>(value: void): ResultAsync<void, E>
export function okAsync<T, E = never>(value: T): ResultAsync<T, E> {
return new ResultAsync(Promise.resolve(new Ok<T, E>(value)))
}
export function errAsync<T = never, E = unknown>(err: E): ResultAsync<T, E>
export function errAsync<T = never, E extends void = void>(err: void): ResultAsync<T, void>
export function errAsync<T = never, E = unknown>(err: E): ResultAsync<T, E> {
return new ResultAsync(Promise.resolve(new Err<T, E>(err)))
}
export const fromPromise = ResultAsync.fromPromise
export const fromSafePromise = ResultAsync.fromSafePromise
export const fromAsyncThrowable = ResultAsync.fromThrowable
// Combines the array of async results into one result.
export type CombineResultAsyncs<
T extends readonly ResultAsync<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? TraverseAsync<UnwrapAsync<T>>
: ResultAsync<ExtractOkAsyncTypes<T>, ExtractErrAsyncTypes<T>[number]>
// Combines the array of async results into one result with all errors.
export type CombineResultsWithAllErrorsArrayAsync<
T extends readonly ResultAsync<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? TraverseWithAllErrorsAsync<UnwrapAsync<T>>
: ResultAsync<ExtractOkAsyncTypes<T>, ExtractErrAsyncTypes<T>[number][]>
// Unwraps the inner `Result` from a `ResultAsync` for all elements.
type UnwrapAsync<T> = IsLiteralArray<T> extends 1
? Writable<T> extends [infer H, ...infer Rest]
? H extends PromiseLike<infer HI>
? HI extends Result<unknown, unknown>
? [Dedup<HI>, ...UnwrapAsync<Rest>]
: never
: never
: []
: // If we got something too general such as ResultAsync<X, Y>[] then we
// simply need to map it to ResultAsync<X[], Y[]>. Yet `ResultAsync`
// itself is a union therefore it would be enough to cast it to Ok.
T extends Array<infer A>
? A extends PromiseLike<infer HI>
? HI extends Result<infer L, infer R>
? Ok<L, R>[]
: never
: never
: never
// Traverse through the tuples of the async results and create one
// `ResultAsync` where the collected tuples are merged.
type TraverseAsync<T, Depth extends number = 5> = IsLiteralArray<T> extends 1
? Combine<T, Depth> extends [infer Oks, infer Errs]
? ResultAsync<EmptyArrayToNever<Oks>, MembersToUnion<Errs>>
: never
: // The following check is important if we somehow reach to the point of
// checking something similar to ResultAsync<X, Y>[]. In this case we don't
// know the length of the elements, therefore we need to traverse the X and Y
// in a way that the result should contain X[] and Y[].
T extends Array<infer I>
? // The MemberListOf<I> here is to include all possible types. Therefore
// if we face (ResultAsync<X, Y> | ResultAsync<A, B>)[] this type should
// handle the case.
Combine<MemberListOf<I>, Depth> extends [infer Oks, infer Errs]
? // The following `extends unknown[]` checks are just to satisfy the TS.
// we already expect them to be an array.
Oks extends unknown[]
? Errs extends unknown[]
? ResultAsync<EmptyArrayToNever<Oks[number][]>, MembersToUnion<Errs[number][]>>
: ResultAsync<EmptyArrayToNever<Oks[number][]>, Errs>
: // The rest of the conditions are to satisfy the TS and support
// the edge cases which are not really expected to happen.
Errs extends unknown[]
? ResultAsync<Oks, MembersToUnion<Errs[number][]>>
: ResultAsync<Oks, Errs>
: never
: never
// This type is similar to the `TraverseAsync` while the errors are also
// collected in a list. For the checks/conditions made here, see that type
// for the documentation.
type TraverseWithAllErrorsAsync<T, Depth extends number = 5> = TraverseAsync<
T,
Depth
> extends ResultAsync<infer Oks, infer Errs>
? ResultAsync<Oks, Errs[]>
: never
// Converts a reaodnly array into a writable array
type Writable<T> = T extends ReadonlyArray<unknown> ? [...T] : T
================================================
FILE: src/result.ts
================================================
import { errAsync, ResultAsync } from './'
import { createNeverThrowError, ErrorConfig } from './_internals/error'
import {
combineResultList,
combineResultListWithAllErrors,
ExtractErrTypes,
ExtractOkTypes,
InferAsyncErrTypes,
InferErrTypes,
InferOkTypes,
} from './_internals/utils'
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Result {
/**
* Wraps a function with a try catch, creating a new function with the same
* arguments but returning `Ok` if successful, `Err` if the function throws
*
* @param fn function to wrap with ok on success or err on failure
* @param errorFn when an error is thrown, this will wrap the error result if provided
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function fromThrowable<Fn extends (...args: readonly any[]) => any, E>(
fn: Fn,
errorFn?: (e: unknown) => E,
): (...args: Parameters<Fn>) => Result<ReturnType<Fn>, E> {
return (...args) => {
try {
const result = fn(...args)
return ok(result)
} catch (e) {
return err(errorFn ? errorFn(e) : e)
}
}
}
export function combine<
T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]
>(resultList: T): CombineResults<T>
export function combine<T extends readonly Result<unknown, unknown>[]>(
resultList: T,
): CombineResults<T>
export function combine<
T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]
>(resultList: T): CombineResults<T> {
return combineResultList(resultList) as CombineResults<T>
}
export function combineWithAllErrors<
T extends readonly [Result<unknown, unknown>, ...Result<unknown, unknown>[]]
>(resultList: T): CombineResultsWithAllErrorsArray<T>
export function combineWithAllErrors<T extends readonly Result<unknown, unknown>[]>(
resultList: T,
): CombineResultsWithAllErrorsArray<T>
export function combineWithAllErrors<T extends readonly Result<unknown, unknown>[]>(
resultList: T,
): CombineResultsWithAllErrorsArray<T> {
return combineResultListWithAllErrors(resultList) as CombineResultsWithAllErrorsArray<T>
}
}
export type Result<T, E> = Ok<T, E> | Err<T, E>
export function ok<T, E = never>(value: T): Ok<T, E>
export function ok<T extends void = void, E = never>(value: void): Ok<void, E>
export function ok<T, E = never>(value: T): Ok<T, E> {
return new Ok(value)
}
export function err<T = never, E extends string = string>(err: E): Err<T, E>
export function err<T = never, E = unknown>(err: E): Err<T, E>
export function err<T = never, E extends void = void>(err: void): Err<T, void>
export function err<T = never, E = unknown>(err: E): Err<T, E> {
return new Err(err)
}
/**
* Evaluates the given generator to a Result returned or an Err yielded from it,
* whichever comes first.
*
* This function is intended to emulate Rust's ? operator.
* See `/tests/safeTry.test.ts` for examples.
*
* @param body - What is evaluated. In body, `yield* result` works as
* Rust's `result?` expression.
* @returns The first occurrence of either an yielded Err or a returned Result.
*/
export function safeTry<T, E>(body: () => Generator<Err<never, E>, Result<T, E>>): Result<T, E>
export function safeTry<
YieldErr extends Err<never, unknown>,
GeneratorReturnResult extends Result<unknown, unknown>
>(
body: () => Generator<YieldErr, GeneratorReturnResult>,
): Result<
InferOkTypes<GeneratorReturnResult>,
InferErrTypes<YieldErr> | InferErrTypes<GeneratorReturnResult>
>
/**
* Evaluates the given generator to a Result returned or an Err yielded from it,
* whichever comes first.
*
* This function is intended to emulate Rust's ? operator.
* See `/tests/safeTry.test.ts` for examples.
*
* @param body - What is evaluated. In body, `yield* result` and
* `yield* resultAsync` work as Rust's `result?` expression.
* @returns The first occurrence of either an yielded Err or a returned Result.
*/
export function safeTry<T, E>(
body: () => AsyncGenerator<Err<never, E>, Result<T, E>>,
): ResultAsync<T, E>
export function safeTry<
YieldErr extends Err<never, unknown>,
GeneratorReturnResult extends Result<unknown, unknown>
>(
body: () => AsyncGenerator<YieldErr, GeneratorReturnResult>,
): ResultAsync<
InferOkTypes<GeneratorReturnResult>,
InferErrTypes<YieldErr> | InferErrTypes<GeneratorReturnResult>
>
export function safeTry<T, E>(
body:
| (() => Generator<Err<never, E>, Result<T, E>>)
| (() => AsyncGenerator<Err<never, E>, Result<T, E>>),
): Result<T, E> | ResultAsync<T, E> {
const n = body().next()
if (n instanceof Promise) {
return new ResultAsync(n.then((r) => r.value))
}
return n.value
}
interface IResult<T, E> {
/**
* Used to check if a `Result` is an `OK`
*
* @returns `true` if the result is an `OK` variant of Result
*/
isOk(): this is Ok<T, E>
/**
* Used to check if a `Result` is an `Err`
*
* @returns `true` if the result is an `Err` variant of Result
*/
isErr(): this is Err<T, E>
/**
* Maps a `Result<T, E>` to `Result<U, E>`
* by applying a function to a contained `Ok` value, leaving an `Err` value
* untouched.
*
* @param f The function to apply an `OK` value
* @returns the result of applying `f` or an `Err` untouched
*/
map<A>(f: (t: T) => A): Result<A, E>
/**
* Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a
* contained `Err` value, leaving an `Ok` value untouched.
*
* This function can be used to pass through a successful result while
* handling an error.
*
* @param f a function to apply to the error `Err` value
*/
mapErr<U>(f: (e: E) => U): Result<T, U>
/**
* Similar to `map` Except you must return a new `Result`.
*
* This is useful for when you need to do a subsequent computation using the
* inner `T` value, but that computation might fail.
* Additionally, `andThen` is really useful as a tool to flatten a
* `Result<Result<A, E2>, E1>` into a `Result<A, E2>` (see example below).
*
* @param f The function to apply to the current value
*/
andThen<R extends Result<unknown, unknown>>(
f: (t: T) => R,
): Result<InferOkTypes<R>, InferErrTypes<R> | E>
andThen<U, F>(f: (t: T) => Result<U, F>): Result<U, E | F>
/**
* This "tee"s the current value to an passed-in computation such as side
* effect functions but still returns the same current value as the result.
*
* This is useful when you want to pass the current result to your side-track
* work such as logging but want to continue main-track work after that.
* This method does not care about the result of the passed in computation.
*
* @param f The function to apply to the current value
*/
andTee(f: (t: T) => unknown): Result<T, E>
/**
* This "tee"s the current `Err` value to an passed-in computation such as side
* effect functions but still returns the same `Err` value as the result.
*
* This is useful when you want to pass the current `Err` value to your side-track
* work such as logging but want to continue error-track work after that.
* This method does not care about the result of the passed in computation.
*
* @param f The function to apply to the current `Err` value
*/
orTee(f: (t: E) => unknown): Result<T, E>
/**
* Similar to `andTee` except error result of the computation will be passed
* to the downstream in case of an error.
*
* This version is useful when you want to make side-effects but in case of an
* error, you want to pass the error to the downstream.
*
* @param f The function to apply to the current value
*/
andThrough<R extends Result<unknown, unknown>>(f: (t: T) => R): Result<T, InferErrTypes<R> | E>
andThrough<F>(f: (t: T) => Result<unknown, F>): Result<T, E | F>
/**
* Takes an `Err` value and maps it to a `Result<T, SomeNewType>`.
*
* This is useful for error recovery.
*
*
* @param f A function to apply to an `Err` value, leaving `Ok` values
* untouched.
*/
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): Result<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A>): Result<U | T, A>
/**
* Similar to `map` Except you must return a new `Result`.
*
* This is useful for when you need to do a subsequent async computation using
* the inner `T` value, but that computation might fail. Must return a ResultAsync
*
* @param f The function that returns a `ResultAsync` to apply to the current
* value
*/
asyncAndThen<U, F>(f: (t: T) => ResultAsync<U, F>): ResultAsync<U, E | F>
/**
* Maps a `Result<T, E>` to `ResultAsync<U, E>`
* by applying an async function to a contained `Ok` value, leaving an `Err`
* value untouched.
*
* @param f An async function to apply an `OK` value
*/
asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E>
/**
* Unwrap the `Ok` value, or return the default if there is an `Err`
*
* @param v the default value to return if there is an `Err`
*/
unwrapOr<A>(v: A): T | A
/**
*
* Given 2 functions (one for the `Ok` variant and one for the `Err` variant)
* execute the function that matches the `Result` variant.
*
* Match callbacks do not necessitate to return a `Result`, however you can
* return a `Result` if you want to.
*
* `match` is like chaining `map` and `mapErr`, with the distinction that
* with `match` both functions must have the same return type.
*
* @param ok
* @param err
*/
match<A, B = A>(ok: (t: T) => A, err: (e: E) => B): A | B
/**
* @deprecated will be removed in 9.0.0.
*
* You can use `safeTry` without this method.
* @example
* ```typescript
* safeTry(function* () {
* const okValue = yield* yourResult
* })
* ```
* Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
*/
safeUnwrap(): Generator<Err<never, E>, T>
/**
* **This method is unsafe, and should only be used in a test environments**
*
* Takes a `Result<T, E>` and returns a `T` when the result is an `Ok`, otherwise it throws a custom object.
*
* @param config
*/
_unsafeUnwrap(config?: ErrorConfig): T
/**
* **This method is unsafe, and should only be used in a test environments**
*
* takes a `Result<T, E>` and returns a `E` when the result is an `Err`,
* otherwise it throws a custom object.
*
* @param config
*/
_unsafeUnwrapErr(config?: ErrorConfig): E
}
export class Ok<T, E> implements IResult<T, E> {
constructor(readonly value: T) {}
isOk(): this is Ok<T, E> {
return true
}
isErr(): this is Err<T, E> {
return !this.isOk()
}
map<A>(f: (t: T) => A): Result<A, E> {
return ok(f(this.value))
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
mapErr<U>(_f: (e: E) => U): Result<T, U> {
return ok(this.value)
}
andThen<R extends Result<unknown, unknown>>(
f: (t: T) => R,
): Result<InferOkTypes<R>, InferErrTypes<R> | E>
andThen<U, F>(f: (t: T) => Result<U, F>): Result<U, E | F>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
andThen(f: any): any {
return f(this.value)
}
andThrough<R extends Result<unknown, unknown>>(f: (t: T) => R): Result<T, InferErrTypes<R> | E>
andThrough<F>(f: (t: T) => Result<unknown, F>): Result<T, E | F>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
andThrough(f: any): any {
return f(this.value).map((_value: unknown) => this.value)
}
andTee(f: (t: T) => unknown): Result<T, E> {
try {
f(this.value)
} catch (e) {
// Tee doesn't care about the error
}
return ok<T, E>(this.value)
}
orTee(_f: (t: E) => unknown): Result<T, E> {
return ok<T, E>(this.value)
}
orElse<R extends Result<unknown, unknown>>(
_f: (e: E) => R,
): Result<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<U, A>(_f: (e: E) => Result<U, A>): Result<U | T, A>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
orElse(_f: any): any {
return ok(this.value)
}
asyncAndThen<U, F>(f: (t: T) => ResultAsync<U, F>): ResultAsync<U, E | F> {
return f(this.value)
}
asyncAndThrough<R extends ResultAsync<unknown, unknown>>(
f: (t: T) => R,
): ResultAsync<T, InferAsyncErrTypes<R> | E>
asyncAndThrough<F>(f: (t: T) => ResultAsync<unknown, F>): ResultAsync<T, E | F>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
asyncAndThrough(f: (t: T) => ResultAsync<unknown, unknown>): any {
return f(this.value).map(() => this.value)
}
asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E> {
return ResultAsync.fromSafePromise(f(this.value))
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
unwrapOr<A>(_v: A): T | A {
return this.value
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
match<A, B = A>(ok: (t: T) => A, _err: (e: E) => B): A | B {
return ok(this.value)
}
safeUnwrap(): Generator<Err<never, E>, T> {
const value = this.value
/* eslint-disable-next-line require-yield */
return (function* () {
return value
})()
}
_unsafeUnwrap(_?: ErrorConfig): T {
return this.value
}
_unsafeUnwrapErr(config?: ErrorConfig): E {
throw createNeverThrowError('Called `_unsafeUnwrapErr` on an Ok', this, config)
}
// eslint-disable-next-line @typescript-eslint/no-this-alias, require-yield
*[Symbol.iterator](): Generator<Err<never, E>, T> {
return this.value
}
}
export class Err<T, E> implements IResult<T, E> {
constructor(readonly error: E) {}
isOk(): this is Ok<T, E> {
return false
}
isErr(): this is Err<T, E> {
return !this.isOk()
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
map<A>(_f: (t: T) => A): Result<A, E> {
return err(this.error)
}
mapErr<U>(f: (e: E) => U): Result<T, U> {
return err(f(this.error))
}
andThrough<F>(_f: (t: T) => Result<unknown, F>): Result<T, E | F> {
return err(this.error)
}
andTee(_f: (t: T) => unknown): Result<T, E> {
return err(this.error)
}
orTee(f: (t: E) => unknown): Result<T, E> {
try {
f(this.error)
} catch (e) {
// Tee doesn't care about the error
}
return err<T, E>(this.error)
}
andThen<R extends Result<unknown, unknown>>(
_f: (t: T) => R,
): Result<InferOkTypes<R>, InferErrTypes<R> | E>
andThen<U, F>(_f: (t: T) => Result<U, F>): Result<U, E | F>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
andThen(_f: any): any {
return err(this.error)
}
orElse<R extends Result<unknown, unknown>>(
f: (e: E) => R,
): Result<InferOkTypes<R> | T, InferErrTypes<R>>
orElse<U, A>(f: (e: E) => Result<U, A>): Result<U | T, A>
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
orElse(f: any): any {
return f(this.error)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asyncAndThen<U, F>(_f: (t: T) => ResultAsync<U, F>): ResultAsync<U, E | F> {
return errAsync<U, E>(this.error)
}
asyncAndThrough<F>(_f: (t: T) => ResultAsync<unknown, F>): ResultAsync<T, E | F> {
return errAsync<T, E>(this.error)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asyncMap<U>(_f: (t: T) => Promise<U>): ResultAsync<U, E> {
return errAsync<U, E>(this.error)
}
unwrapOr<A>(v: A): T | A {
return v
}
match<A, B = A>(_ok: (t: T) => A, err: (e: E) => B): A | B {
return err(this.error)
}
safeUnwrap(): Generator<Err<never, E>, T> {
const error = this.error
return (function* () {
yield err(error)
throw new Error('Do not use this generator out of `safeTry`')
})()
}
_unsafeUnwrap(config?: ErrorConfig): T {
throw createNeverThrowError('Called `_unsafeUnwrap` on an Err', this, config)
}
_unsafeUnwrapErr(_?: ErrorConfig): E {
return this.error
}
*[Symbol.iterator](): Generator<Err<never, E>, T> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this
// @ts-expect-error -- This is structurally equivalent and safe
yield self
// @ts-expect-error -- This is structurally equivalent and safe
return self
}
}
export const fromThrowable = Result.fromThrowable
//#region Combine - Types
// This is a helper type to prevent infinite recursion in typing rules.
//
// Use this with your `depth` variable in your types.
type Prev = [
never,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
...0[]
]
// Collects the results array into separate tuple array.
//
// T - The array of the results
// Collected - The collected tuples.
// Depth - The maximum depth.
type CollectResults<T, Collected extends unknown[] = [], Depth extends number = 50> = [
Depth,
] extends [never]
? []
: T extends [infer H, ...infer Rest]
? // And test whether the head of the list is a result
H extends Result<infer L, infer R>
? // Continue collecting...
CollectResults<
// the rest of the elements
Rest,
// The collected
[...Collected, [L, R]],
// and one less of the current depth
Prev[Depth]
>
: never // Impossible
: Collected
// Transposes an array
//
// A - The array source
// Transposed - The collected transposed array
// Depth - The maximum depth.
export type Transpose<
A,
Transposed extends unknown[][] = [],
Depth extends number = 10
> = A extends [infer T, ...infer Rest]
? T extends [infer L, infer R]
? Transposed extends [infer PL, infer PR]
? PL extends unknown[]
? PR extends unknown[]
? Transpose<Rest, [[...PL, L], [...PR, R]], Prev[Depth]>
: never
: never
: Transpose<Rest, [[L], [R]], Prev[Depth]>
: Transposed
: Transposed
// Combines the both sides of the array of the results into a tuple of the
// union of the ok types and the union of the err types.
//
// T - The array of the results
// Depth - The maximum depth.
export type Combine<T, Depth extends number = 5> = Transpose<CollectResults<T>, [], Depth> extends [
infer L,
infer R,
]
? [UnknownMembersToNever<L>, UnknownMembersToNever<R>]
: Transpose<CollectResults<T>, [], Depth> extends []
? [[], []]
: never
// Deduplicates the result, as the result type is a union of Err and Ok types.
export type Dedup<T> = T extends Result<infer RL, infer RR>
? [unknown] extends [RL]
? Err<RL, RR>
: Ok<RL, RR>
: T
// Given a union, this gives the array of the union members.
export type MemberListOf<T> = (
(T extends unknown ? (t: T) => T : never) extends infer U
? (U extends unknown ? (u: U) => unknown : never) extends (v: infer V) => unknown
? V
: never
: never
) extends (_: unknown) => infer W
? [...MemberListOf<Exclude<T, W>>, W]
: []
// Converts an empty array to never.
//
// The second type parameter here will affect how to behave to `never[]`s.
// If a precise type is required, pass `1` here so that it will resolve
// a literal array such as `[ never, never ]`. Otherwise, set `0` or the default
// type value will cause this to resolve the arrays containing only `never`
// items as `never` only.
export type EmptyArrayToNever<T, NeverArrayToNever extends number = 0> = T extends []
? never
: NeverArrayToNever extends 1
? T extends [never, ...infer Rest]
? [EmptyArrayToNever<Rest>] extends [never]
? never
: T
: T
: T
// Converts the `unknown` items of an array to `never`s.
type UnknownMembersToNever<T> = T extends [infer H, ...infer R]
? [[unknown] extends [H] ? never : H, ...UnknownMembersToNever<R>]
: T
// Gets the member type of the array or never.
export type MembersToUnion<T> = T extends unknown[] ? T[number] : never
// Checks if the given type is a literal array.
export type IsLiteralArray<T> = T extends { length: infer L }
? L extends number
? number extends L
? 0
: 1
: 0
: 0
// Traverses an array of results and returns a single result containing
// the oks and errs union-ed/combined.
type Traverse<T, Depth extends number = 5> = Combine<T, Depth> extends [infer Oks, infer Errs]
? Result<EmptyArrayToNever<Oks, 1>, MembersToUnion<Errs>>
: never
// Traverses an array of results and returns a single result containing
// the oks combined and the array of errors combined.
type TraverseWithAllErrors<T, Depth extends number = 5> = Traverse<T, Depth> extends Result<
infer Oks,
infer Errs
>
? Result<Oks, Errs[]>
: never
// Combines the array of results into one result.
export type CombineResults<
T extends readonly Result<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? Traverse<T>
: Result<ExtractOkTypes<T>, ExtractErrTypes<T>[number]>
// Combines the array of results into one result with all errors.
export type CombineResultsWithAllErrorsArray<
T extends readonly Result<unknown, unknown>[]
> = IsLiteralArray<T> extends 1
? TraverseWithAllErrors<T>
: Result<ExtractOkTypes<T>, ExtractErrTypes<T>[number][]>
//#endregion
================================================
FILE: tests/index.test.ts
================================================
import * as td from 'testdouble'
import {
err,
Err,
errAsync,
fromAsyncThrowable,
fromPromise,
fromSafePromise,
fromThrowable,
ok,
Ok,
okAsync,
Result,
ResultAsync,
} from '../src'
import { vitest, describe, expect, it } from 'vitest'
describe('Result.Ok', () => {
it('Creates an Ok value', () => {
const okVal = ok(12)
expect(okVal.isOk()).toBe(true)
expect(okVal.isErr()).toBe(false)
expect(okVal).toBeInstanceOf(Ok)
})
it('Creates an Ok value with null', () => {
const okVal = ok(null)
expect(okVal.isOk()).toBe(true)
expect(okVal.isErr()).toBe(false)
expect(okVal._unsafeUnwrap()).toBe(null)
})
it('Creates an Ok value with undefined', () => {
const okVal = ok(undefined)
expect(okVal.isOk()).toBe(true)
expect(okVal.isErr()).toBe(false)
expect(okVal._unsafeUnwrap()).toBeUndefined()
})
it('Is comparable', () => {
expect(ok(42)).toEqual(ok(42))
expect(ok(42)).not.toEqual(ok(43))
})
it('Maps over an Ok value', () => {
const okVal = ok(12)
const mapFn = vitest.fn((number) => number.toString())
const mapped = okVal.map(mapFn)
expect(mapped.isOk()).toBe(true)
expect(mapped._unsafeUnwrap()).toBe('12')
expect(mapFn).toHaveBeenCalledTimes(1)
})
it('Skips `mapErr`', () => {
const mapErrorFunc = vitest.fn((_error) => 'mapped error value')
const notMapped = ok(12).mapErr(mapErrorFunc)
expect(notMapped.isOk()).toBe(true)
expect(mapErrorFunc).not.toHaveBeenCalledTimes(1)
})
describe('andThen', () => {
it('Maps to an Ok', () => {
const okVal = ok(12)
const flattened = okVal.andThen((_number) => {
// ...
// complex logic
// ...
return ok({ data: 'why not' })
})
expect(flattened.isOk()).toBe(true)
expect(flattened._unsafeUnwrap()).toStrictEqual({ data: 'why not' })
})
it('Maps to an Err', () => {
const okval = ok(12)
const flattened = okval.andThen((_number) => {
// ...
// complex logic
// ...
return err('Whoopsies!')
})
expect(flattened.isOk()).toBe(false)
const nextFn = vitest.fn((_val) => ok('noop'))
flattened.andThen(nextFn)
expect(nextFn).not.toHaveBeenCalled()
})
})
describe('andThrough', () => {
it('Calls the passed function but returns an original ok', () => {
const okVal = ok(12)
const passedFn = vitest.fn((_number) => ok(undefined))
const thrued = okVal.andThrough(passedFn)
expect(thrued.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(thrued._unsafeUnwrap()).toStrictEqual(12)
})
it('Maps to an Err', () => {
const okval = ok(12)
const thrued = okval.andThen((_number) => {
// ...
// complex logic
// ...
return err('Whoopsies!')
})
expect(thrued.isOk()).toBe(false)
expect(thrued._unsafeUnwrapErr()).toStrictEqual('Whoopsies!')
const nextFn = vitest.fn((_val) => ok('noop'))
thrued.andThen(nextFn)
expect(nextFn).not.toHaveBeenCalled()
})
})
describe('andTee', () => {
it('Calls the passed function but returns an original ok', () => {
const okVal = ok(12)
const passedFn = vitest.fn((_number) => {})
const teed = okVal.andTee(passedFn)
expect(teed.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrap()).toStrictEqual(12)
})
it('returns an original ok even when the passed function fails', () => {
const okVal = ok(12)
const passedFn = vitest.fn((_number) => {
throw new Error('OMG!')
})
const teed = okVal.andTee(passedFn)
expect(teed.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrap()).toStrictEqual(12)
})
})
describe('orTee', () => {
it('Calls the passed function but returns an original err', () => {
const errVal = err(12)
const passedFn = vitest.fn((_number) => {})
const teed = errVal.orTee(passedFn)
expect(teed.isErr()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrapErr()).toStrictEqual(12)
})
it('returns an original err even when the passed function fails', () => {
const errVal = err(12)
const passedFn = vitest.fn((_number) => {
throw new Error('OMG!')
})
const teed = errVal.orTee(passedFn)
expect(teed.isErr()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrapErr()).toStrictEqual(12)
})
})
describe('asyncAndThrough', () => {
it('Calls the passed function but returns an original ok as Async', async () => {
const okVal = ok(12)
const passedFn = vitest.fn((_number) => okAsync(undefined))
const teedAsync = okVal.asyncAndThrough(passedFn)
expect(teedAsync).toBeInstanceOf(ResultAsync)
const teed = await teedAsync
expect(teed.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrap()).toStrictEqual(12)
})
it('Maps to an Err', async () => {
const okval = ok(12)
const teedAsync = okval.asyncAndThen((_number) => {
// ...
// complex logic
// ...
return errAsync('Whoopsies!')
})
expect(teedAsync).toBeInstanceOf(ResultAsync)
const teed = await teedAsync
expect(teed.isOk()).toBe(false)
expect(teed._unsafeUnwrapErr()).toStrictEqual('Whoopsies!')
const nextFn = vitest.fn((_val) => ok('noop'))
teed.andThen(nextFn)
expect(nextFn).not.toHaveBeenCalled()
})
})
describe('orElse', () => {
it('Skips orElse on an Ok value', () => {
const okVal = ok(12)
const errorCallback = vitest.fn((_errVal) => err<number, string>('It is now a string'))
expect(okVal.orElse(errorCallback)).toEqual(ok(12))
expect(errorCallback).not.toHaveBeenCalled()
})
})
it('unwrapOr and return the Ok value', () => {
const okVal = ok(12)
expect(okVal.unwrapOr(1)).toEqual(12)
})
it('Maps to a ResultAsync', async () => {
const okVal = ok(12)
const flattened = okVal.asyncAndThen((_number) => {
// ...
// complex async logic
// ...
return okAsync({ data: 'why not' })
})
expect(flattened).toBeInstanceOf(ResultAsync)
const newResult = await flattened
expect(newResult.isOk()).toBe(true)
expect(newResult._unsafeUnwrap()).toStrictEqual({ data: 'why not' })
})
it('Maps to a promise', async () => {
const asyncMapper = vitest.fn((_val) => {
// ...
// complex logic
// ..
// db queries
// network calls
// disk io
// etc ...
return Promise.resolve('Very Nice!')
})
const okVal = ok(12)
const promise = okVal.asyncMap(asyncMapper)
expect(promise).toBeInstanceOf(ResultAsync)
const newResult = await promise
expect(newResult.isOk()).toBe(true)
expect(asyncMapper).toHaveBeenCalledTimes(1)
expect(newResult._unsafeUnwrap()).toStrictEqual('Very Nice!')
})
it('Matches on an Ok', () => {
const okMapper = vitest.fn((_val) => 'weeeeee')
const errMapper = vitest.fn((_val) => 'wooooo')
const matched = ok(12).match(okMapper, errMapper)
expect(matched).toBe('weeeeee')
expect(okMapper).toHaveBeenCalledTimes(1)
expect(errMapper).not.toHaveBeenCalled()
})
it('Unwraps without issue', () => {
const okVal = ok(12)
expect(okVal._unsafeUnwrap()).toBe(12)
})
it('Can read the value after narrowing', () => {
const fallible: () => Result<string, number> = () => ok('safe to read')
const val = fallible()
// After this check we val is narrowed to Ok<string, number>. Without this
// line TypeScript will not allow accessing val.value.
if (val.isErr()) return
expect(val.value).toBe('safe to read')
})
})
describe('Result.Err', () => {
it('Creates an Err value', () => {
const errVal = err('I have you now.')
expect(errVal.isOk()).toBe(false)
expect(errVal.isErr()).toBe(true)
expect(errVal).toBeInstanceOf(Err)
})
it('Is comparable', () => {
expect(err(42)).toEqual(err(42))
expect(err(42)).not.toEqual(err(43))
})
it('Skips `map`', () => {
const errVal = err('I am your father')
const mapper = vitest.fn((_value) => 'noooo')
const hopefullyNotMapped = errVal.map(mapper)
expect(hopefullyNotMapped.isErr()).toBe(true)
expect(mapper).not.toHaveBeenCalled()
expect(hopefullyNotMapped._unsafeUnwrapErr()).toEqual(errVal._unsafeUnwrapErr())
})
it('Maps over an Err', () => {
const errVal = err('Round 1, Fight!')
const mapper = vitest.fn((error: string) => error.replace('1', '2'))
const mapped = errVal.mapErr(mapper)
expect(mapped.isErr()).toBe(true)
expect(mapper).toHaveBeenCalledTimes(1)
expect(mapped._unsafeUnwrapErr()).not.toEqual(errVal._unsafeUnwrapErr())
})
it('unwrapOr and return the default value', () => {
const okVal = err<number, string>('Oh nooo')
expect(okVal.unwrapOr(1)).toEqual(1)
})
it('Skips over andThen', () => {
const errVal = err('Yolo')
const mapper = vitest.fn((_val) => ok<string, string>('yooyo'))
const hopefullyNotFlattened = errVal.andThen(mapper)
expect(hopefullyNotFlattened.isErr()).toBe(true)
expect(mapper).not.toHaveBeenCalled()
expect(errVal._unsafeUnwrapErr()).toEqual('Yolo')
})
it('Skips over andThrough', () => {
const errVal = err('Yolo')
const mapper = vitest.fn((_val) => ok<void, string>(undefined))
const hopefullyNotFlattened = errVal.andThrough(mapper)
expect(hopefullyNotFlattened.isErr()).toBe(true)
expect(mapper).not.toHaveBeenCalled()
expect(errVal._unsafeUnwrapErr()).toEqual('Yolo')
})
it('Skips over andTee', () => {
const errVal = err('Yolo')
const mapper = vitest.fn((_val) => {})
const hopefullyNotFlattened = errVal.andTee(mapper)
expect(hopefullyNotFlattened.isErr()).toBe(true)
expect(mapper).not.toHaveBeenCalled()
expect(errVal._unsafeUnwrapErr()).toEqual('Yolo')
})
it('Skips over asyncAndThrough but returns ResultAsync instead', async () => {
const errVal = err('Yolo')
const mapper = vitest.fn((_val) => okAsync<string, unknown>('Async'))
const hopefullyNotFlattened = errVal.asyncAndThrough(mapper)
expect(hopefullyNotFlattened).toBeInstanceOf(ResultAsync)
const result = await hopefullyNotFlattened
expect(result.isErr()).toBe(true)
expect(mapper).not.toHaveBeenCalled()
expect(result._unsafeUnwrapErr()).toEqual('Yolo')
})
it('Transforms error into ResultAsync within `asyncAndThen`', async () => {
const errVal = err('Yolo')
const asyncMapper = vitest.fn((_val) => okAsync<string, string>('yooyo'))
const hopefullyNotFlattened = errVal.asyncAndThen(asyncMapper)
expect(hopefullyNotFlattened).toBeInstanceOf(ResultAsync)
expect(asyncMapper).not.toHaveBeenCalled()
const syncResult = await hopefullyNotFlattened
expect(syncResult._unsafeUnwrapErr()).toEqual('Yolo')
})
it('Does not invoke callback within `asyncMap`', async () => {
const asyncMapper = vitest.fn((_val) => {
// ...
// complex logic
// ..
// db queries
// network calls
// disk io
// etc ...
return Promise.resolve('Very Nice!')
})
const errVal = err('nooooooo')
const promise = errVal.asyncMap(asyncMapper)
expect(promise).toBeInstanceOf(ResultAsync)
const sameResult = await promise
expect(sameResult.isErr()).toBe(true)
expect(asyncMapper).not.toHaveBeenCalled()
expect(sameResult._unsafeUnwrapErr()).toEqual(errVal._unsafeUnwrapErr())
})
it('Matches on an Err', () => {
const okMapper = vitest.fn((_val) => 'weeeeee')
const errMapper = vitest.fn((_val) => 'wooooo')
const matched = err(12).match(okMapper, errMapper)
expect(matched).toBe('wooooo')
expect(okMapper).not.toHaveBeenCalled()
expect(errMapper).toHaveBeenCalledTimes(1)
})
it('Throws when you unwrap an Err', () => {
const errVal = err('woopsies')
expect(() => {
errVal._unsafeUnwrap()
}).toThrowError()
})
it('Unwraps without issue', () => {
const okVal = err(12)
expect(okVal._unsafeUnwrapErr()).toBe(12)
})
describe('orElse', () => {
it('invokes the orElse callback on an Err value', () => {
const okVal = err('BOOOM!')
const errorCallback = vitest.fn((_errVal) => err(true))
expect(okVal.orElse(errorCallback)).toEqual(err(true))
expect(errorCallback).toHaveBeenCalledTimes(1)
})
})
})
describe('Result.fromThrowable', () => {
it('Creates a function that returns an OK result when the inner function does not throw', () => {
const hello = (): string => 'hello'
const safeHello = Result.fromThrowable(hello)
const result = hello()
const safeResult = safeHello()
expect(safeResult).toBeInstanceOf(Ok)
expect(result).toEqual(safeResult._unsafeUnwrap())
})
// Added for issue #300 -- the test here is not so much that expectations are met as that the test compiles.
it('Accepts an inner function which takes arguments', () => {
const hello = (fname: string): string => `hello, ${fname}`
const safeHello = Result.fromThrowable(hello)
const result = hello('Dikembe')
const safeResult = safeHello('Dikembe')
expect(safeResult).toBeInstanceOf(Ok)
expect(result).toEqual(safeResult._unsafeUnwrap())
})
it('Creates a function that returns an err when the inner function throws', () => {
const thrower = (): string => {
throw new Error()
}
// type: () => Result<string, unknown>
// received types from thrower fn, no errorFn is provides therefore Err type is unknown
const safeThrower = Result.fromThrowable(thrower)
const result = safeThrower()
expect(result).toBeInstanceOf(Err)
expect(result._unsafeUnwrapErr()).toBeInstanceOf(Error)
})
it('Accepts an error handler as a second argument', () => {
const thrower = (): string => {
throw new Error()
}
type MessageObject = { message: string }
const toMessageObject = (): MessageObject => ({ message: 'error' })
// type: () => Result<string, MessageObject>
// received types from thrower fn and errorFn return type
const safeThrower = Result.fromThrowable(thrower, toMessageObject)
const result = safeThrower()
expect(result.isOk()).toBe(false)
expect(result.isErr()).toBe(true)
expect(result).toBeInstanceOf(Err)
expect(result._unsafeUnwrapErr()).toEqual({ message: 'error' })
})
it('has a top level export', () => {
expect(fromThrowable).toBe(Result.fromThrowable)
})
})
describe('Utils', () => {
describe('`Result.combine`', () => {
describe('Synchronous `combine`', () => {
it('Combines a list of results into an Ok value', () => {
const resultList = [ok(123), ok(456), ok(789)]
const result = Result.combine(resultList)
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toEqual([123, 456, 789])
})
it('Combines a list of results into an Err value', () => {
const resultList: Result<number, string>[] = [
ok(123),
err('boooom!'),
ok(456),
err('ahhhhh!'),
]
const result = Result.combine(resultList)
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe('boooom!')
})
it('Combines heterogeneous lists', () => {
type HeterogenousList = [
Result<string, string>,
Result<number, number>,
Result<boolean, boolean>,
]
const heterogenousList: HeterogenousList = [ok('Yooooo'), ok(123), ok(true)]
type ExpecteResult = Result<[string, number, boolean], string | number | boolean>
const result: ExpecteResult = Result.combine(heterogenousList)
expect(result._unsafeUnwrap()).toEqual(['Yooooo', 123, true])
})
it('Does not destructure / concatenate arrays', () => {
type HomogenousList = [Result<string[], boolean>, Result<number[], string>]
const homogenousList: HomogenousList = [ok(['hello', 'world']), ok([1, 2, 3])]
type ExpectedResult = Result<[string[], number[]], boolean | string>
const result: ExpectedResult = Result.combine(homogenousList)
expect(result._unsafeUnwrap()).toEqual([
['hello', 'world'],
[1, 2, 3],
])
})
})
describe('`ResultAsync.combine`', () => {
it('Combines a list of async results into an Ok value', async () => {
const asyncResultList = [okAsync(123), okAsync(456), okAsync(789)]
const resultAsync: ResultAsync<number[], never[]> = ResultAsync.combine(asyncResultList)
expect(resultAsync).toBeInstanceOf(ResultAsync)
const result = await ResultAsync.combine(asyncResultList)
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toEqual([123, 456, 789])
})
it('Combines a list of results into an Err value', async () => {
const resultList: ResultAsync<number, string>[] = [
okAsync(123),
errAsync('boooom!'),
okAsync(456),
errAsync('ahhhhh!'),
]
const result = await ResultAsync.combine(resultList)
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe('boooom!')
})
it('Combines heterogeneous lists', async () => {
type HeterogenousList = [
ResultAsync<string, string>,
ResultAsync<number, number>,
ResultAsync<boolean, boolean>,
ResultAsync<number[], string>,
]
const heterogenousList: HeterogenousList = [
okAsync('Yooooo'),
okAsync(123),
okAsync(true),
okAsync([1, 2, 3]),
]
type ExpecteResult = Result<[string, number, boolean, number[]], string | number | boolean>
const result: ExpecteResult = await ResultAsync.combine(heterogenousList)
expect(result._unsafeUnwrap()).toEqual(['Yooooo', 123, true, [1, 2, 3]])
})
})
})
describe('`Result.combineWithAllErrors`', () => {
describe('Synchronous `combineWithAllErrors`', () => {
it('Combines a list of results into an Ok value', () => {
const resultList = [ok(123), ok(456), ok(789)]
const result = Result.combineWithAllErrors(resultList)
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toEqual([123, 456, 789])
})
it('Combines a list of results into an Err value', () => {
const resultList: Result<number, string>[] = [
ok(123),
err('boooom!'),
ok(456),
err('ahhhhh!'),
]
const result = Result.combineWithAllErrors(resultList)
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toEqual(['boooom!', 'ahhhhh!'])
})
it('Combines heterogeneous lists', () => {
type HeterogenousList = [
Result<string, string>,
Result<number, number>,
Result<boolean, boolean>,
]
const heterogenousList: HeterogenousList = [ok('Yooooo'), ok(123), ok(true)]
type ExpecteResult = Result<[string, number, boolean], (string | number | boolean)[]>
const result: ExpecteResult = Result.combineWithAllErrors(heterogenousList)
expect(result._unsafeUnwrap()).toEqual(['Yooooo', 123, true])
})
it('Does not destructure / concatenate arrays', () => {
type HomogenousList = [Result<string[], boolean>, Result<number[], string>]
const homogenousList: HomogenousList = [ok(['hello', 'world']), ok([1, 2, 3])]
type ExpectedResult = Result<[string[], number[]], (boolean | string)[]>
const result: ExpectedResult = Result.combineWithAllErrors(homogenousList)
expect(result._unsafeUnwrap()).toEqual([
['hello', 'world'],
[1, 2, 3],
])
})
})
describe('`ResultAsync.combineWithAllErrors`', () => {
it('Combines a list of async results into an Ok value', async () => {
const asyncResultList = [okAsync(123), okAsync(456), okAsync(789)]
const result = await ResultAsync.combineWithAllErrors(asyncResultList)
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toEqual([123, 456, 789])
})
it('Combines a list of results into an Err value', async () => {
const asyncResultList: ResultAsync<number, string>[] = [
okAsync(123),
errAsync('boooom!'),
okAsync(456),
errAsync('ahhhhh!'),
]
const result = await ResultAsync.combineWithAllErrors(asyncResultList)
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toEqual(['boooom!', 'ahhhhh!'])
})
it('Combines heterogeneous lists', async () => {
type HeterogenousList = [
ResultAsync<string, string>,
ResultAsync<number, number>,
ResultAsync<boolean, boolean>,
]
const heterogenousList: HeterogenousList = [okAsync('Yooooo'), okAsync(123), okAsync(true)]
type ExpecteResult = Result<[string, number, boolean], (string | number | boolean)[]>
const result: ExpecteResult = await ResultAsync.combineWithAllErrors(heterogenousList)
expect(result._unsafeUnwrap()).toEqual(['Yooooo', 123, true])
})
})
describe('testdouble `ResultAsync.combine`', () => {
interface ITestInterface {
getName(): string
setName(name: string): void
getAsyncResult(): ResultAsync<ITestInterface, Error>
}
it('Combines `testdouble` proxies from mocks generated via interfaces', async () => {
const mock = td.object<ITestInterface>()
const result = await ResultAsync.combine([okAsync(mock)] as const)
expect(result).toBeDefined()
expect(result.isErr()).toBeFalsy()
const unwrappedResult = result._unsafeUnwrap()
expect(unwrappedResult.length).toBe(1)
expect(unwrappedResult[0]).toBe(mock)
})
})
})
})
describe('ResultAsync', () => {
it('Is awaitable to a Result', async () => {
// For a success value
const asyncVal = okAsync(12)
expect(asyncVal).toBeInstanceOf(ResultAsync)
const val = await asyncVal
expect(val).toBeInstanceOf(Ok)
expect(val._unsafeUnwrap()).toEqual(12)
// For an error
const asyncErr = errAsync('Wrong format')
expect(asyncErr).toBeInstanceOf(ResultAsync)
const err = await asyncErr
expect(err).toBeInstanceOf(Err)
expect(err._unsafeUnwrapErr()).toEqual('Wrong format')
})
describe('acting as a Promise<Result>', () => {
it('Is chainable like any Promise', async () => {
// For a success value
const asyncValChained = okAsync(12).then((res) => {
if (res.isOk()) {
return res.value + 2
}
})
expect(asyncValChained).toBeInstanceOf(Promise)
const val = await asyncValChained
expect(val).toEqual(14)
// For an error
const asyncErrChained = errAsync('Oops').then((res) => {
if (res.isErr()) {
return res.error + '!'
}
})
expect(asyncErrChained).toBeInstanceOf(Promise)
const err = await asyncErrChained
expect(err).toEqual('Oops!')
})
it('Can be used with Promise.all', async () => {
const allResult = await Promise.all([okAsync<string, Error>('1')])
expect(allResult).toHaveLength(1)
expect(allResult[0]).toBeInstanceOf(Ok)
if (!(allResult[0] instanceof Ok)) return
expect(allResult[0].isOk()).toBe(true)
expect(allResult[0]._unsafeUnwrap()).toEqual('1')
})
it('rejects if the underlying promise is rejected', () => {
const asyncResult = new ResultAsync(Promise.reject('oops'))
expect(asyncResult).rejects.toBe('oops')
})
})
describe('map', () => {
it('Maps a value using a synchronous function', async () => {
const asyncVal = okAsync(12)
const mapSyncFn = vitest.fn((number) => number.toString())
const mapped = asyncVal.map(mapSyncFn)
expect(mapped).toBeInstanceOf(ResultAsync)
const newVal = await mapped
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe('12')
expect(mapSyncFn).toHaveBeenCalledTimes(1)
})
it('Maps a value using an asynchronous function', async () => {
const asyncVal = okAsync(12)
const mapAsyncFn = vitest.fn((number) => Promise.resolve(number.toString()))
const mapped = asyncVal.map(mapAsyncFn)
expect(mapped).toBeInstanceOf(ResultAsync)
const newVal = await mapped
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe('12')
expect(mapAsyncFn).toHaveBeenCalledTimes(1)
})
it('Skips an error', async () => {
const asyncErr = errAsync<number, string>('Wrong format')
const mapSyncFn = vitest.fn((number) => number.toString())
const notMapped = asyncErr.map(mapSyncFn)
expect(notMapped).toBeInstanceOf(ResultAsync)
const newVal = await notMapped
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(mapSyncFn).toHaveBeenCalledTimes(0)
})
})
describe('mapErr', () => {
it('Maps an error using a synchronous function', async () => {
const asyncErr = errAsync('Wrong format')
const mapErrSyncFn = vitest.fn((str) => 'Error: ' + str)
const mappedErr = asyncErr.mapErr(mapErrSyncFn)
expect(mappedErr).toBeInstanceOf(ResultAsync)
const newVal = await mappedErr
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Error: Wrong format')
expect(mapErrSyncFn).toHaveBeenCalledTimes(1)
})
it('Maps an error using an asynchronous function', async () => {
const asyncErr = errAsync('Wrong format')
const mapErrAsyncFn = vitest.fn((str) => Promise.resolve('Error: ' + str))
const mappedErr = asyncErr.mapErr(mapErrAsyncFn)
expect(mappedErr).toBeInstanceOf(ResultAsync)
const newVal = await mappedErr
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Error: Wrong format')
expect(mapErrAsyncFn).toHaveBeenCalledTimes(1)
})
it('Skips a value', async () => {
const asyncVal = okAsync(12)
const mapErrSyncFn = vitest.fn((str) => 'Error: ' + str)
const notMapped = asyncVal.mapErr(mapErrSyncFn)
expect(notMapped).toBeInstanceOf(ResultAsync)
const newVal = await notMapped
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe(12)
expect(mapErrSyncFn).toHaveBeenCalledTimes(0)
})
})
describe('andThen', () => {
it('Maps a value using a function returning a ResultAsync', async () => {
const asyncVal = okAsync(12)
const andThenResultAsyncFn = vitest.fn(() => okAsync('good'))
const mapped = asyncVal.andThen(andThenResultAsyncFn)
expect(mapped).toBeInstanceOf(ResultAsync)
const newVal = await mapped
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe('good')
expect(andThenResultAsyncFn).toHaveBeenCalledTimes(1)
})
it('Maps a value using a function returning a Result', async () => {
const asyncVal = okAsync(12)
const andThenResultFn = vitest.fn(() => ok('good'))
const mapped = asyncVal.andThen(andThenResultFn)
expect(mapped).toBeInstanceOf(ResultAsync)
const newVal = await mapped
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe('good')
expect(andThenResultFn).toHaveBeenCalledTimes(1)
})
it('Skips an Error', async () => {
const asyncVal = errAsync<string, string>('Wrong format')
const andThenResultFn = vitest.fn(() => ok<string, string>('good'))
const notMapped = asyncVal.andThen(andThenResultFn)
expect(notMapped).toBeInstanceOf(ResultAsync)
const newVal = await notMapped
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(andThenResultFn).toHaveBeenCalledTimes(0)
})
})
describe('andThrough', () => {
it('Returns the original value when map function returning ResultAsync succeeds', async () => {
const asyncVal = okAsync(12)
/*
A couple examples of this function
DB persistence (create or update)
API calls (create or update)
*/
const andThroughResultAsyncFn = vitest.fn(() => okAsync('good'))
const thrued = asyncVal.andThrough(andThroughResultAsyncFn)
expect(thrued).toBeInstanceOf(ResultAsync)
const result = await thrued
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toBe(12)
expect(andThroughResultAsyncFn).toHaveBeenCalledTimes(1)
})
it('Maps to an error when map function returning ResultAsync fails', async () => {
const asyncVal = okAsync(12)
const andThroughResultAsyncFn = vitest.fn(() => errAsync('oh no!'))
const thrued = asyncVal.andThrough(andThroughResultAsyncFn)
expect(thrued).toBeInstanceOf(ResultAsync)
const result = await thrued
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe('oh no!')
expect(andThroughResultAsyncFn).toHaveBeenCalledTimes(1)
})
it('Returns the original value when map function returning Result succeeds', async () => {
const asyncVal = okAsync(12)
const andThroughResultFn = vitest.fn(() => ok('good'))
const thrued = asyncVal.andThrough(andThroughResultFn)
expect(thrued).toBeInstanceOf(ResultAsync)
const newVal = await thrued
expect(newVal.isOk()).toBe(true)
expect(newVal._unsafeUnwrap()).toBe(12)
expect(andThroughResultFn).toHaveBeenCalledTimes(1)
})
it('Maps to an error when map function returning Result fails', async () => {
const asyncVal = okAsync(12)
const andThroughResultFn = vitest.fn(() => err('oh no!'))
const thrued = asyncVal.andThrough(andThroughResultFn)
expect(thrued).toBeInstanceOf(ResultAsync)
const newVal = await thrued
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('oh no!')
expect(andThroughResultFn).toHaveBeenCalledTimes(1)
})
it('Skips an Error', async () => {
const asyncVal = errAsync<string, string>('Wrong format')
const andThroughResultFn = vitest.fn(() => ok<string, string>('good'))
const notMapped = asyncVal.andThrough(andThroughResultFn)
expect(notMapped).toBeInstanceOf(ResultAsync)
const newVal = await notMapped
expect(newVal.isErr()).toBe(true)
expect(newVal._unsafeUnwrapErr()).toBe('Wrong format')
expect(andThroughResultFn).toHaveBeenCalledTimes(0)
})
})
describe('andTee', () => {
it('Calls the passed function but returns an original ok', async () => {
const okVal = okAsync(12)
const passedFn = vitest.fn((_number) => {})
const teed = await okVal.andTee(passedFn)
expect(teed.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrap()).toStrictEqual(12)
})
it('returns an original ok even when the passed function fails', async () => {
const okVal = okAsync(12)
const passedFn = vitest.fn((_number) => {
throw new Error('OMG!')
})
const teed = await okVal.andTee(passedFn)
expect(teed.isOk()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrap()).toStrictEqual(12)
})
})
describe('orTee', () => {
it('Calls the passed function but returns an original err', async () => {
const errVal = errAsync(12)
const passedFn = vitest.fn((_number) => {})
const teed = await errVal.orTee(passedFn)
expect(teed.isErr()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrapErr()).toStrictEqual(12)
})
it('returns an original err even when the passed function fails', async () => {
const errVal = errAsync(12)
const passedFn = vitest.fn((_number) => {
throw new Error('OMG!')
})
const teed = await errVal.orTee(passedFn)
expect(teed.isErr()).toBe(true)
expect(passedFn).toHaveBeenCalledTimes(1)
expect(teed._unsafeUnwrapErr()).toStrictEqual(12)
})
})
describe('orElse', () => {
it('Skips orElse on an Ok value', async () => {
const okVal = okAsync(12)
const errorCallback = vitest.fn((_errVal) => errAsync<number, string>('It is now a string'))
const result = await okVal.orElse(errorCallback)
expect(result).toEqual(ok(12))
expect(errorCallback).not.toHaveBeenCalled()
})
it('Invokes the orElse callback on an Err value', async () => {
const myResult = errAsync('BOOOM!')
const errorCallback = vitest.fn((_errVal) => errAsync(true))
const result = await myResult.orElse(errorCallback)
expect(result).toEqual(err(true))
expect(errorCallback).toHaveBeenCalledTimes(1)
})
it('Accepts a regular Result in the callback', async () => {
const myResult = errAsync('BOOOM!')
const errorCallback = vitest.fn((_errVal) => err(true))
const result = await myResult.orElse(errorCallback)
expect(result).toEqual(err(true))
expect(errorCallback).toHaveBeenCalledTimes(1)
})
})
describe('match', () => {
it('Matches on an Ok', async () => {
const okMapper = vitest.fn((_val) => 'weeeeee')
const errMapper = vitest.fn((_val) => 'wooooo')
const matched = await okAsync(12).match(okMapper, errMapper)
expect(matched).toBe('weeeeee')
expect(okMapper).toHaveBeenCalledTimes(1)
expect(errMapper).not.toHaveBeenCalled()
})
it('Matches on an Error', async () => {
const okMapper = vitest.fn((_val) => 'weeeeee')
const errMapper = vitest.fn((_val) => 'wooooo')
const matched = await errAsync('bad').match(okMapper, errMapper)
expect(matched).toBe('wooooo')
expect(okMapper).not.toHaveBeenCalled()
expect(errMapper).toHaveBeenCalledTimes(1)
})
})
describe('unwrapOr', () => {
it('returns a promise to the result value on an Ok', async () => {
const unwrapped = await okAsync(12).unwrapOr(10)
expect(unwrapped).toBe(12)
})
it('returns a promise to the provided default value on an Error', async () => {
const unwrapped = await errAsync<number, number>(12).unwrapOr(10)
expect(unwrapped).toBe(10)
})
})
describe('fromSafePromise', () => {
it('Creates a ResultAsync from a Promise', async () => {
const res = ResultAsync.fromSafePromise(Promise.resolve(12))
expect(res).toBeInstanceOf(ResultAsync)
const val = await res
expect(val.isOk()).toBe(true)
expect(val._unsafeUnwrap()).toEqual(12)
})
it('has a top level export', () => {
expect(fromSafePromise).toBe(ResultAsync.fromSafePromise)
})
})
describe('fromPromise', () => {
it('Accepts an error handler as a second argument', async () => {
const res = ResultAsync.fromPromise(Promise.reject('No!'), (e) => new Error('Oops: ' + e))
expect(res).toBeInstanceOf(ResultAsync)
const val = await res
expect(val.isErr()).toBe(true)
expect(val._unsafeUnwrapErr()).toEqual(Error('Oops: No!'))
})
it('has a top level export', () => {
expect(fromPromise).toBe(ResultAsync.fromPromise)
})
})
describe('ResultAsync.fromThrowable', () => {
it('creates a new function that returns a ResultAsync', async () => {
const example = ResultAsync.fromThrowable(async (a: number, b: number) => a + b)
const res = example(4, 8)
expect(res).toBeInstanceOf(ResultAsync)
const val = await res
expect(val.isOk()).toBe(true)
expect(val._unsafeUnwrap()).toEqual(12)
})
it('handles synchronous errors', async () => {
const example = ResultAsync.fromThrowable(() => {
if (1 > 0) throw new Error('Oops: No!')
return Promise.resolve(12)
})
const val = await example()
expect(val.isErr()).toBe(true)
expect(val._unsafeUnwrapErr()).toEqual(Error('Oops: No!'))
})
it('handles asynchronous errors', async () => {
const example = ResultAsync.fromThrowable(async () => {
if (1 > 0) throw new Error('Oops: No!')
return 12
})
const val = await example()
expect(val.isErr()).toBe(true)
expect(val._unsafeUnwrapErr()).toEqual(Error('Oops: No!'))
})
it('Accepts an error handler as a second argument', async () => {
const example = ResultAsync.fromThrowable(
() => Promise.reject('No!'),
(e) => new Error('Oops: ' + e),
)
const val = await example()
expect(val.isErr()).toBe(true)
expect(val._unsafeUnwrapErr()).toEqual(TypeError('Oops: No!'))
})
it('has a top level export', () => {
expect(fromAsyncThrowable).toBe(ResultAsync.fromThrowable)
})
})
describe('okAsync', () => {
it('Creates a ResultAsync that resolves to an Ok', async () => {
const val = okAsync(12)
expect(val).toBeInstanceOf(ResultAsync)
const res = await val
expect(res.isOk()).toBe(true)
expect(res._unsafeUnwrap()).toEqual(12)
})
})
describe('errAsync', () => {
it('Creates a ResultAsync that resolves to an Err', async () => {
const err = errAsync('bad')
expect(err).toBeInstanceOf(ResultAsync)
const res = await err
expect(res.isErr()).toBe(true)
expect(res._unsafeUnwrapErr()).toEqual('bad')
})
})
})
================================================
FILE: tests/safe-try.test.ts
================================================
import {
safeTry,
ok,
okAsync,
err,
errAsync,
Ok,
Err,
Result,
ResultAsync,
} from "../src"
import { describe, expect, test } from 'vitest'
describe('Returns what is returned from the generator function', () => {
const val = "value"
test("With synchronous Ok", () => {
const res = safeTry(function*() {
return ok(val)
})
expect(res).toBeInstanceOf(Ok)
expect(res._unsafeUnwrap()).toBe(val)
})
test("With synchronous Err", () => {
const res = safeTry(function*() {
return err(val)
})
expect(res).toBeInstanceOf(Err)
expect(res._unsafeUnwrapErr()).toBe(val)
})
test("With async Ok", async () => {
const res = await safeTry(async function*() {
return await okAsync(val)
})
expect(res).toBeInstanceOf(Ok)
expect(res._unsafeUnwrap()).toBe(val)
})
test("With async Err", async () => {
const res = await safeTry(async function*() {
return await errAsync(val)
})
expect(res).toBeInstanceOf(Err)
expect(res._unsafeUnwrapErr()).toBe(val)
})
})
describe("Returns the first occurence of Err instance as yiled*'s operand", () => {
test("With synchronous results", () => {
const errVal = "err"
const okValues = Array<string>()
const result = safeTry(function*() {
const okFoo = yield* ok("foo").safeUnwrap()
okValues.push(okFoo)
const okBar = yield* ok("bar").safeUnwrap()
okValues.push(okBar)
yield* err(errVal).safeUnwrap()
throw new Error("This line should not be executed")
})
expect(okValues).toMatchObject(["foo", "bar"])
expect(result).toBeInstanceOf(Err)
expect(result._unsafeUnwrapErr()).toBe(errVal)
})
test("With async results", async () => {
const errVal = "err"
const okValues = Array<string>()
const result = await safeTry(async function*() {
const okFoo = yield* okAsync("foo").safeUnwrap()
okValues.push(okFoo)
const okBar = yield* okAsync("bar").safeUnwrap()
okValues.push(okBar)
yield* errAsync(errVal).safeUnwrap()
throw new Error("This line should not be executed")
})
expect(okValues).toMatchObject(["foo", "bar"])
expect(result).toBeInstanceOf(Err)
expect(result._unsafeUnwrapErr()).toBe(errVal)
})
test("Mix results of synchronous and async in AsyncGenerator", async () => {
const errVal = "err"
const okValues = Array<string>()
const result = await safeTry(async function*() {
const okFoo = yield* okAsync("foo").safeUnwrap()
okValues.push(okFoo)
const okBar = yield* ok("bar").safeUnwrap()
okValues.push(okBar)
yield* err(errVal).safeUnwrap()
throw new Error("This line should not be executed")
})
expect(okValues).toMatchObject(["foo", "bar"])
expect(result).toBeInstanceOf(Err)
expect(result._unsafeUnwrapErr()).toBe(errVal)
})
})
describe("Tests if README's examples work", () => {
const okValue = 3
const errValue = "err!"
function good(): Result<number, string> {
return ok(okValue)
}
function bad(): Result<number, string> {
return err(errValue)
}
function promiseGood(): Promise<Result<number, string>> {
return Promise.resolve(ok(okValue))
}
function promiseBad(): Promise<Result<number, string>> {
return Promise.resolve(err(errValue))
}
function asyncGood(): ResultAsync<number, string> {
return okAsync(okValue)
}
function asyncBad(): ResultAsync<number, string> {
return errAsync(errValue)
}
test("mayFail2 error", () => {
function myFunc(): Result<number, string> {
return safeTry<number, string>(function*() {
return ok(
(yield* good()
.mapErr(e => `1st, ${e}`)
.safeUnwrap())
+
(yield* bad()
.mapErr(e => `2nd, ${e}`)
.safeUnwrap())
)
})
}
const result = myFunc()
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe(`2nd, ${errValue}`)
})
test("all ok", () => {
function myFunc(): Result<number, string> {
return safeTry<number, string>(function*() {
return ok(
(yield* good()
.mapErr(e => `1st, ${e}`)
.safeUnwrap())
+
(yield* good()
.mapErr(e => `2nd, ${e}`)
.safeUnwrap())
)
})
}
const result = myFunc()
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toBe(okValue + okValue)
})
test("async mayFail1 error", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function*() {
return ok(
(yield* (await promiseBad())
.mapErr(e => `1st, ${e}`)
.safeUnwrap())
+
(yield* asyncGood()
.mapErr(e => `2nd, ${e}`)
.safeUnwrap())
)
})
}
const result = await myFunc()
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe(`1st, ${errValue}`)
})
test("async mayFail2 error", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function*() {
return ok(
(yield* (await promiseGood())
.mapErr(e => `1st, ${e}`)
.safeUnwrap())
+
(yield* asyncBad()
.mapErr(e => `2nd, ${e}`)
.safeUnwrap())
)
})
}
const result = await myFunc()
expect(result.isErr()).toBe(true)
expect(result._unsafeUnwrapErr()).toBe(`2nd, ${errValue}`)
})
test("promise async all ok", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function*() {
return ok(
(yield* (await promiseGood())
.mapErr(e => `1st, ${e}`)
.safeUnwrap())
+
(yield* asyncGood()
.mapErr(e => `2nd, ${e}`)
.safeUnwrap())
)
})
}
const result = await myFunc()
expect(result.isOk()).toBe(true)
expect(result._unsafeUnwrap()).toBe(okValue + okValue)
})
})
describe("it yields and works without safeUnwrap", () => {
test("With synchronous Ok", () => {
const res: Result<string, string> = ok("ok");
const actual = safeTry(function* () {
const x = yield* res;
return ok(x);
});
expect(actual).toBeInstanceOf(Ok);
expect(actual._unsafeUnwrap()).toBe("ok");
});
test("With synchronous Err", () => {
const res: Result<number, string> = err("error");
const actual = safeTry(function* () {
const x = yield* res;
return ok(x);
});
expect(actual).toBeInstanceOf(Err);
expect(actual._unsafeUnwrapErr()).toBe("error");
});
const okValue = 3;
const errValue = "err!";
function good(): Result<number, string> {
return ok(okValue);
}
function bad(): Result<number, string> {
return err(errValue);
}
function promiseGood(): Promise<Result<number, string>> {
return Promise.resolve(ok(okValue));
}
function promiseBad(): Promise<Result<number, string>> {
return Promise.resolve(err(errValue));
}
function asyncGood(): ResultAsync<number, string> {
return okAsync(okValue);
}
function asyncBad(): ResultAsync<number, string> {
return errAsync(errValue);
}
test("mayFail2 error", () => {
function fn(): Result<number, string> {
return safeTry<number, string>(function* () {
const first = yield* good().mapErr((e) => `1st, ${e}`);
const second = yield* bad().mapErr((e) => `2nd, ${e}`);
return ok(first + second);
});
}
const result = fn();
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr()).toBe(`2nd, ${errValue}`);
});
test("all ok", () => {
function myFunc(): Result<number, string> {
return safeTry<number, string>(function* () {
const first = yield* good().mapErr((e) => `1st, ${e}`);
const second = yield* good().mapErr((e) => `2nd, ${e}`);
return ok(first + second);
});
}
const result = myFunc();
expect(result.isOk()).toBe(true);
expect(result._unsafeUnwrap()).toBe(okValue + okValue);
});
test("async mayFail1 error", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function* () {
const first = yield* (await promiseBad()).mapErr((e) => `1st, ${e}`);
const second = yield* asyncGood().mapErr((e) => `2nd, ${e}`);
return ok(first + second);
});
}
const result = await myFunc();
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr()).toBe(`1st, ${errValue}`);
});
test("async mayFail2 error", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function* () {
const goodResult = await promiseGood();
const value = yield* goodResult.mapErr((e) => `1st, ${e}`);
const value2 = yield* asyncBad().mapErr((e) => `2nd, ${e}`);
return okAsync(value + value2);
});
}
const result = await myFunc();
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr()).toBe(`2nd, ${errValue}`);
});
test("promise async all ok", async () => {
function myFunc(): ResultAsync<number, string> {
return safeTry<number, string>(async function* () {
const first = yield* (await promiseGood()).mapErr((e) => `1st, ${e}`);
const second = yield* asyncGood().mapErr((e) => `2nd, ${e}`);
return ok(first + second);
});
}
const result = await myFunc();
expect(result.isOk()).toBe(true);
expect(result._unsafeUnwrap()).toBe(okValue + okValue);
});
})
================================================
FILE: tests/tsconfig.tests.json
================================================
{
"compilerOptions": {
"target": "es2016",
"module": "ES2015",
"noImplicitAny": true,
"sourceMap": false,
"downlevelIteration": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"strictNullChecks": true,
"strictFunctionTypes": true,
"declaration": true,
"moduleResolution": "Node",
"baseUrl": "./src",
"lib": [
"dom",
"es2016",
"es2017.object"
],
"outDir": "dist",
"skipLibCheck": true
},
"include": [
"./index.test.ts",
"./typecheck-tests.ts"
]
}
================================================
FILE: tests/typecheck-tests.ts
================================================
/*
* Type Tests
*
* This file is ran during CI to ensure that there aren't breaking changes with types
*/
import {
err,
errAsync,
fromSafePromise,
ok,
okAsync,
Result,
ResultAsync,
} from '../src'
import { safeTry, Transpose } from '../src/result'
import { type N, Test } from 'ts-toolbelt'
type CreateTuple<L, V = string> =
// Length must always be a number
L extends number
? N.IsNegative<L> extends 1
// Length must always be non-negative
? never
// base case
: L extends 0
? []
// recursion depth check
// typescript has a limit.
: N.Lower<L, 50> extends 1
? [V, ...CreateTuple<N.Sub<L, 1>, V>]
: never
: never;
(function describe(_ = 'Result') {
(function describe(_ = 'andThen') {
(function it(_ = 'Combines two equal error types (native scalar types)') {
type Expectation = Result<unknown, string>
const result: Expectation = ok<number, string>(123)
.andThen((val) => err('yoooooo dude' + val))
});
(function it(_ = 'Combines two equal error types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<string, MyError>
const result: Expectation = ok<number, MyError>(123)
.andThen((val) => err<string, MyError>({ stack: '/blah', code: 500 }))
});
(function it(_ = 'Creates a union of error types for disjoint types') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<string, MyError | string[]>
const result: Expectation = ok<number, MyError>(123)
.andThen((val) => err<string, string[]>(['oh nooooo']))
});
(function it(_ = 'Infers error type when returning disjoint types (native scalar types)') {
type Expectation = Result<unknown, string | number | boolean>
const result: Expectation = ok<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err(false)
}
})
});
(function it(_ = 'Infers error type when returning disjoint types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<unknown, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'Infers new ok type when returning both Ok and Err (same as initial)') {
type Expectation = Result<number, unknown>
const result: Expectation = ok<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok(val + 456)
}
})
});
(function it(_ = 'Infers new ok type when returning both Ok and Err (different from initial)') {
const initial = ok<number, string>(123)
type Expectation = Result<string, unknown>
const result: Expectation = initial
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok(val + ' string')
}
})
});
(function it(_ = 'Infers new err type when returning both Ok and Err') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<unknown, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return ok(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'allows specifying the E and T types explicitly') {
type Expectation = Result<'yo', number>
const result: Expectation = ok(123).andThen<'yo', number>(val => {
return ok('yo')
})
});
});
(function describe(_ = 'andThrough') {
(function it(_ = 'Combines two equal error types (native scalar types)') {
type Expectation = Result<number, string>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => err('yoooooo dude' + val))
});
(function it(_ = 'Combines two equal error types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, MyError>
const result: Expectation = ok<number, MyError>(123)
.andThrough((val) => err<string, MyError>({ stack: '/blah', code: 500 }))
});
(function it(_ = 'Creates a union of error types for disjoint types') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, MyError | string[]>
const result: Expectation = ok<number, MyError>(123)
.andThrough((val) => err<string, string[]>(['oh nooooo']))
});
(function it(_ = 'Infers error type when returning disjoint types (native scalar types)') {
type Expectation = Result<number, string | number | boolean>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err(false)
}
})
});
(function it(_ = 'Infers error type when returning disjoint types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'Returns the original ok type when returning both Ok and Err (same as initial)') {
type Expectation = Result<number, unknown>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok(val + 456)
}
})
});
(function it(_ = 'Returns the original ok type when returning both Ok and Err (different from initial)') {
const initial = ok<number, string>(123)
type Expectation = Result<number, unknown>
const result: Expectation = initial
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok("Hi" + val)
}
})
});
(function it(_ = 'Infers new err type when returning both Ok and Err') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return ok(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'allows specifying the E type explicitly') {
type Expectation = Result<number, string>
const result: Expectation = ok(123).andThrough<string>(val => {
return ok('yo')
})
});
});
(function describe(_ = 'orElse') {
(function it(_ = 'the type of the argument is the error type of the result') {
type Expectation = string
const result = ok<number, string>(123)
.orElse((val: Expectation) => {
switch (val) {
case '2':
return err(1)
default:
return err(1)
}
})
});
(function it(_ = 'infers the err return type with multiple returns (same type) ') {
type Expectation = Result<number, number>
const result: Expectation = ok<number, string>(123)
.orElse((val) => {
switch (val) {
case '2':
return err(1)
default:
return err(1)
}
})
});
(function it(_ = 'infers the err return type with multiple returns (different type) ') {
type Expectation = Result<number, number | string>
const result: Expectation = ok<number, string>(123)
.orElse((val) => {
switch (val) {
case '2':
return err(1)
default:
return err('1')
}
})
});
(function it(_ = 'infers ok and err return types with multiple returns ') {
type Expectation = Result<number, number | string>
const result: Expectation = ok<number, string>(123)
.orElse((val) => {
switch (val) {
case '1':
return ok(1)
case '2':
return err(1)
default:
return err('1')
}
})
});
(function it(_ = 'allows specifying the E and T types explicitly') {
type Expectation = Result<'yo', string>
const result: Expectation = ok<'yo', number>('yo').orElse<'yo', string>(val => {
return err('yo')
})
});
(function it(_ = 'Creates a union of ok types for disjoint types') {
type Expectation = Result<string | number, boolean>
const result: Expectation = err<string, boolean[]>([true])
.orElse((val) => ok<string, boolean>('recovered!'))
});
(function it(_ = 'Infers ok type when returning disjoint types') {
type Expectation = Result<string | number | boolean, unknown>
const result: Expectation = err<string, number>(123)
.orElse((val) => {
switch (val) {
case 1:
return ok('yoooooo dude' + val)
case 2:
return ok(123)
default:
return ok(false)
}
})
});
(function it(_ = 'Infers new type when returning both Ok and Err') {
const initial = err<string, number>(123)
type Expectation = Result<string | true, false>
const result: Expectation = initial
.orElse((val) => {
switch (val) {
case 1:
return err(false as const)
default:
return ok(true as const)
}
})
});
});
(function describe(_ = 'match') {
(function it(_ = 'the type of the arguments match the types of the result') {
type OKExpectation = number
type ErrExpectation = string
ok<number, string>(123)
.match(
(val: OKExpectation): void => void val,
(val: ErrExpectation): void => void val,
);
err<number, string>("123")
.match(
(val: OKExpectation): void => void val,
(val: ErrExpectation): void => void val,
);
});
(function it(_ = 'infers the resulting value from match callbacks (same type)') {
type Expectation = boolean
const okResult: Expectation = ok<number, string>(123)
.match(
(val) => !!val,
(val) => !!val,
);
const errResult: Expectation = err<number, string>('123')
.match(
(val) => !!val,
(val) => !!val,
);
});
(function it(_ = 'infers the resulting value from match callbacks (different type)') {
type Expectation = boolean | bigint
const okResult: Expectation = ok<string, number>('123')
.match(
(val) => !!val,
(val) => BigInt(val),
);
const errResult: Expectation = err<string, number>(123)
.match(
(val) => !!val,
(val) => BigInt(val),
);
});
});
(function describe(_ = 'asyncAndThen') {
(function it(_ = 'Combines two equal error types (native scalar types)') {
type Expectation = ResultAsync<unknown, string>
const result: Expectation = ok<number, string>(123)
.asyncAndThen((val) => errAsync('yoooooo dude' + val))
});
(function it(_ = 'Combines two equal error types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<string, MyError>
const result: Expectation = ok<number, MyError>(123)
.asyncAndThen((val) => errAsync<string, MyError>({ stack: '/blah', code: 500 }))
});
(function it(_ = 'Creates a union of error types for disjoint types') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<string, MyError | string[]>
const result: Expectation = ok<number, MyError>(123)
.asyncAndThen((val) => errAsync<string, string[]>(['oh nooooo']))
});
});
(function describe(_ = 'asyncAndThrough') {
(function it(_ = 'Combines two equal error types (native scalar types)') {
type Expectation = ResultAsync<unknown, string>
const result: Expectation = ok<number, string>(123)
.asyncAndThrough((val) => errAsync('yoooooo dude' + val))
});
(function it(_ = 'Combines two equal error types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<number, MyError>
const result: Expectation = ok<number, MyError>(123)
.asyncAndThrough((val) => errAsync<string, MyError>({ stack: '/blah', code: 500 }))
});
(function it(_ = 'Creates a union of error types for disjoint types') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<number, MyError | string[]>
const result: Expectation = ok<number, MyError>(123)
.asyncAndThrough((val) => errAsync<string, string[]>(['oh nooooo']))
});
(function it(_ = 'Infers error type when returning disjoint types (native scalar types)') {
type Expectation = ResultAsync<number, string | number | boolean>
const result: Expectation = ok<number, string>(123)
.asyncAndThrough((val) => {
switch (val) {
case 1:
return errAsync('yoooooo dude' + val)
case 2:
return errAsync(123)
default:
return errAsync(false)
}
})
});
(function it(_ = 'Infers error type when returning disjoint types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'Returns the original ok type when returning both Ok and Err (same as initial)') {
type Expectation = Result<number, unknown>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok(val + 456)
}
})
});
(function it(_ = 'Returns the original ok type when returning both Ok and Err (different from initial)') {
const initial = ok<number, string>(123)
type Expectation = Result<number, unknown>
const result: Expectation = initial
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
default:
return ok("Hi" + val)
}
})
});
(function it(_ = 'Infers new err type when returning both Ok and Err') {
interface MyError {
stack: string
code: number
}
type Expectation = Result<number, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.andThrough((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return ok(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'allows specifying the E type explicitly') {
type Expectation = Result<number, string>
const result: Expectation = ok(123).andThrough<string>(val => {
return ok('yo')
})
});
(function it(_ = 'Infers new err type when returning both Ok and Err') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<number, string | number | MyError>
const result: Expectation = ok<number, string>(123)
.asyncAndThrough((val) => {
switch (val) {
case 1:
return errAsync('yoooooo dude' + val)
case 2:
return okAsync(123)
default:
return errAsync({ stack: '/blah', code: 500 })
}
})
});
});
(function describe(_ = 'combine') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, boolean, boolean ], Error | string | string[]>;
const result = Result.combine([
ok<number, string>(1),
ok<string, string>('string'),
err<boolean, string[]>([ 'string', 'string2' ]),
err<boolean, Error>(new Error('error content')),
])
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], never>;
const result = Result.combine([
ok(1),
ok('string'),
]);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], number | 'abc'>;
const result = Result.combine([
err(1),
err('abc'),
]);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines empty list results into one') {
type Expectation = Result<never, never>;
const results: [] = [];
const result = Result.combine(results);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines arrays of results to a result of an array') {
type Expectation = Result<string[], string>;
const results: Result<string, string>[] = [];
const result = Result.combine(results);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function describe(_ = 'inference on large tuples') {
(function it(_ = 'Should correctly infer the type on tuples with 6 elements') {
type Input = CreateTuple<6, Result<string, never>>
type Expectation = Result<CreateTuple<6, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 7 elements') {
type Input = CreateTuple<7, Result<string, never>>
type Expectation = Result<CreateTuple<7, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 8 elements') {
type Input = CreateTuple<8, Result<string, never>>
type Expectation = Result<CreateTuple<8, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 9 elements') {
type Input = CreateTuple<9, Result<string, never>>
type Expectation = Result<CreateTuple<9, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 10 elements') {
type Input = CreateTuple<10, Result<string, never>>
type Expectation = Result<CreateTuple<10, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 11 elements') {
type Input = CreateTuple<11, Result<string, never>>
type Expectation = Result<CreateTuple<11, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 12 elements') {
type Input = CreateTuple<12, Result<string, never>>
type Expectation = Result<CreateTuple<12, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 13 elements') {
type Input = CreateTuple<13, Result<string, never>>
type Expectation = Result<CreateTuple<13, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 14 elements') {
type Input = CreateTuple<14, Result<string, never>>
type Expectation = Result<CreateTuple<14, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 15 elements') {
type Input = CreateTuple<15, Result<string, never>>
type Expectation = Result<CreateTuple<15, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 16 elements') {
type Input = CreateTuple<16, Result<string, never>>
type Expectation = Result<CreateTuple<16, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 17 elements') {
type Input = CreateTuple<17, Result<string, never>>
type Expectation = Result<CreateTuple<17, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 18 elements') {
type Input = CreateTuple<18, Result<string, never>>
type Expectation = Result<CreateTuple<18, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 19 elements') {
type Input = CreateTuple<19, Result<string, never>>
type Expectation = Result<CreateTuple<19, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 20 elements') {
type Input = CreateTuple<20, Result<string, never>>
type Expectation = Result<CreateTuple<20, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 21 elements') {
type Input = CreateTuple<21, Result<string, never>>
type Expectation = Result<CreateTuple<21, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 22 elements') {
type Input = CreateTuple<22, Result<string, never>>
type Expectation = Result<CreateTuple<22, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 23 elements') {
type Input = CreateTuple<23, Result<string, never>>
type Expectation = Result<CreateTuple<23, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 24 elements') {
type Input = CreateTuple<24, Result<string, never>>
type Expectation = Result<CreateTuple<24, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 25 elements') {
type Input = CreateTuple<25, Result<string, never>>
type Expectation = Result<CreateTuple<25, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 26 elements') {
type Input = CreateTuple<26, Result<string, never>>
type Expectation = Result<CreateTuple<26, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 27 elements') {
type Input = CreateTuple<27, Result<string, never>>
type Expectation = Result<CreateTuple<27, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 28 elements') {
type Input = CreateTuple<28, Result<string, never>>
type Expectation = Result<CreateTuple<28, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 29 elements') {
type Input = CreateTuple<29, Result<string, never>>
type Expectation = Result<CreateTuple<29, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 30 elements') {
type Input = CreateTuple<30, Result<string, never>>
type Expectation = Result<CreateTuple<30, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 31 elements') {
type Input = CreateTuple<31, Result<string, never>>
type Expectation = Result<CreateTuple<31, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 32 elements') {
type Input = CreateTuple<32, Result<string, never>>
type Expectation = Result<CreateTuple<32, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 33 elements') {
type Input = CreateTuple<33, Result<string, never>>
type Expectation = Result<CreateTuple<33, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 34 elements') {
type Input = CreateTuple<34, Result<string, never>>
type Expectation = Result<CreateTuple<34, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 35 elements') {
type Input = CreateTuple<35, Result<string, never>>
type Expectation = Result<CreateTuple<35, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 36 elements') {
type Input = CreateTuple<36, Result<string, never>>
type Expectation = Result<CreateTuple<36, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 37 elements') {
type Input = CreateTuple<37, Result<string, never>>
type Expectation = Result<CreateTuple<37, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 38 elements') {
type Input = CreateTuple<38, Result<string, never>>
type Expectation = Result<CreateTuple<38, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 39 elements') {
type Input = CreateTuple<39, Result<string, never>>
type Expectation = Result<CreateTuple<39, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 40 elements') {
type Input = CreateTuple<40, Result<string, never>>
type Expectation = Result<CreateTuple<40, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 41 elements') {
type Input = CreateTuple<41, Result<string, never>>
type Expectation = Result<CreateTuple<41, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 42 elements') {
type Input = CreateTuple<42, Result<string, never>>
type Expectation = Result<CreateTuple<42, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 43 elements') {
type Input = CreateTuple<43, Result<string, never>>
type Expectation = Result<CreateTuple<43, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 44 elements') {
type Input = CreateTuple<44, Result<string, never>>
type Expectation = Result<CreateTuple<44, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 45 elements') {
type Input = CreateTuple<45, Result<string, never>>
type Expectation = Result<CreateTuple<45, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 46 elements') {
type Input = CreateTuple<46, Result<string, never>>
type Expectation = Result<CreateTuple<46, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 47 elements') {
type Input = CreateTuple<47, Result<string, never>>
type Expectation = Result<CreateTuple<47, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 48 elements') {
type Input = CreateTuple<48, Result<string, never>>
type Expectation = Result<CreateTuple<48, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 49 elements') {
type Input = CreateTuple<49, Result<string, never>>
type Expectation = Result<CreateTuple<49, string>, never>
const inputValues = input<Input>()
const result = Result.combine(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
});
});
(function describe(_ = 'combineWithAllErrors') {
(function it(_ = 'combines different results into one') {
type Expectation = Result<[ number, string, never, never ], (string[] | Error)[]>;
const result = Result.combineWithAllErrors([
ok(1),
ok('string'),
err([ 'string', 'string2' ]),
err(new Error('error content')),
]);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines only ok results into one') {
type Expectation = Result<[ number, string ], never[]>;
const result = Result.combineWithAllErrors([
ok(1),
ok('string'),
]);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines only err results into one') {
type Expectation = Result<[ never, never ], (number | 'string')[]>;
const result = Result.combineWithAllErrors([
err(1),
err('string'),
]);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines arrays of results to a result of an array') {
type Expectation = Result<string[], (number | string)[]>;
const results: Result<string, number | string>[] = [];
const result = Result.combineWithAllErrors(results);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function it(_ = 'combines arrays of different results to a result of an array') {
type Expectation = Result<(string | boolean)[], (number | string)[]>;
const results: (Result<string, number> | Result<boolean, string>)[] = [];
const result = Result.combineWithAllErrors(results);
const assignableToCheck: Expectation = result;
const assignablefromCheck: typeof result = assignableToCheck;
});
(function describe(_ = 'inference on large tuples') {
(function it(_ = 'Should correctly infer the type on tuples with 6 elements') {
type Input = CreateTuple<6, Result<string, number>>
type Expectation = Result<CreateTuple<6, string>, number[]>
const inputValues = input<Input>()
const result = Result.combineWithAllErrors(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 15 elements') {
type Input = CreateTuple<15, Result<string, number>>
type Expectation = Result<CreateTuple<15, string>, number[]>
const inputValues = input<Input>()
const result = Result.combineWithAllErrors(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 30 elements') {
type Input = CreateTuple<30, Result<string, number>>
type Expectation = Result<CreateTuple<30, string>, number[]>
const inputValues = input<Input>()
const result = Result.combineWithAllErrors(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
(function it(_ = 'Should correctly infer the type on tuples with 49 elements') {
type Input = CreateTuple<49 , Result<string, number>>
type Expectation = Result<CreateTuple<49, string>, number[]>
const inputValues = input<Input>()
const result = Result.combineWithAllErrors(inputValues)
Test.checks([
Test.check<typeof result, Expectation, Test.Pass>(),
])
});
});
});
(function describe(_ = 'err') {
(function it(_ = 'infers the error type narrowly when it is a string') {
type Expectation = Result<never, 'error'>
const result = err('error')
const assignableToCheck: Expectation = result;
});
(function it(_ = 'infers the error type widely when it is not a string') {
type Expectation = Result<never, { abc: number }>
const result = err({ abc: 123 })
const assignableToCheck: Expectation = result;
});
})
});
(function describe(_ = 'ResultAsync') {
(function describe(_ = 'andThen') {
(function it(_ = 'Combines two equal error types (native scalar types)') {
type Expectation = ResultAsync<unknown, string>
const result: Expectation = okAsync<number, string>(123)
.andThen((val) => err('yoooooo dude' + val))
});
(function it(_ = 'Combines two equal error types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<string, MyError>
const result: Expectation = okAsync<number, MyError>(123)
.andThen((val) => err<string, MyError>({ stack: '/blah', code: 500 }))
});
(function it(_ = 'Creates a union of error types for disjoint types') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<string, MyError | string[]>
const result: Expectation = okAsync<number, MyError>(123)
.andThen((val) => err<string, string[]>(['oh nooooo']))
});
(function describe(_ = 'when returning Result types') {
(function it(_ = 'Infers error type when returning disjoint types (native scalar types)') {
type Expectation = ResultAsync<unknown, string | number | boolean>
const result: Expectation = okAsync<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err(false)
}
})
});
(function it(_ = 'Infers error type when returning disjoint types (custom types)') {
interface MyError {
stack: string
code: number
}
type Expectation = ResultAsync<unknown, string | number | MyError>
const result: Expectation = okAsync<number, string>(123)
.andThen((val) => {
switch (val) {
case 1:
return err('yoooooo dude' + val)
case 2:
return err(123)
default:
return err({ stack: '/blah', code: 500 })
}
})
});
(function it(_ = 'Infers new ok type when returning both Ok and Err (same as initial)') {
type Expectation = ResultAsync<number, unknown>
const result: Expectation = okAsync<number, string>(123)
gitextract_7c6yg_7y/ ├── .babelrc ├── .changeset/ │ └── config.json ├── .eslintrc.js ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── rollup.config.mjs ├── src/ │ ├── _internals/ │ │ ├── error.ts │ │ └── utils.ts │ ├── index.ts │ ├── result-async.ts │ └── result.ts ├── tests/ │ ├── index.test.ts │ ├── safe-try.test.ts │ ├── tsconfig.tests.json │ └── typecheck-tests.ts └── tsconfig.json
SYMBOL INDEX (462 symbols across 7 files)
FILE: src/_internals/error.ts
type ErrorConfig (line 3) | interface ErrorConfig {
type NeverThrowError (line 11) | interface NeverThrowError<T, E> {
FILE: src/_internals/utils.ts
type ExtractOkTypes (line 5) | type ExtractOkTypes<T extends readonly Result<unknown, unknown>[]> = {
type ExtractOkAsyncTypes (line 10) | type ExtractOkAsyncTypes<T extends readonly ResultAsync<unknown, unknown...
type ExtractErrTypes (line 15) | type ExtractErrTypes<T extends readonly Result<unknown, unknown>[]> = {
type ExtractErrAsyncTypes (line 20) | type ExtractErrAsyncTypes<T extends readonly ResultAsync<unknown, unknow...
type InferOkTypes (line 24) | type InferOkTypes<R> = R extends Result<infer T, unknown> ? T : never
type InferErrTypes (line 25) | type InferErrTypes<R> = R extends Result<unknown, infer E> ? E : never
type InferAsyncOkTypes (line 27) | type InferAsyncOkTypes<R> = R extends ResultAsync<infer T, unknown> ? T ...
type InferAsyncErrTypes (line 28) | type InferAsyncErrTypes<R> = R extends ResultAsync<unknown, infer E> ? E...
FILE: src/result-async.ts
class ResultAsync (line 22) | class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
method constructor (line 25) | constructor(res: Promise<Result<T, E>>) {
method fromSafePromise (line 30) | static fromSafePromise<T, E = never>(promise: Promise<T>): ResultAsync...
method fromPromise (line 37) | static fromPromise<T, E>(promise: Promise<T>, errorFn: (e: unknown) =>...
method fromThrowable (line 46) | static fromThrowable<A extends readonly any[], R, E>(
method combine (line 69) | static combine<T extends readonly ResultAsync<unknown, unknown>[]>(
method combineWithAllErrors (line 81) | static combineWithAllErrors<T extends readonly ResultAsync<unknown, un...
method map (line 89) | map<A>(f: (t: T) => A | Promise<A>): ResultAsync<A, E> {
method andThrough (line 101) | andThrough<F>(f: (t: T) => Result<unknown, F> | ResultAsync<unknown, F...
method andTee (line 117) | andTee(f: (t: T) => unknown): ResultAsync<T, E> {
method orTee (line 133) | orTee(f: (t: E) => unknown): ResultAsync<T, E> {
method mapErr (line 149) | mapErr<U>(f: (e: E) => U | Promise<U>): ResultAsync<T, U> {
method andThen (line 169) | andThen(f: any): any {
method orElse (line 190) | orElse(f: any): any {
method match (line 202) | match<A, B = A>(ok: (t: T) => A, _err: (e: E) => B): Promise<A | B> {
method unwrapOr (line 206) | unwrapOr<A>(t: A): Promise<T | A> {
method safeUnwrap (line 222) | async *safeUnwrap(): AsyncGenerator<Err<never, E>, T> {
method then (line 227) | then<A, B>(
method [Symbol.asyncIterator] (line 234) | async *[Symbol.asyncIterator](): AsyncGenerator<Err<never, E>, T> {
function okAsync (line 249) | function okAsync<T, E = never>(value: T): ResultAsync<T, E> {
function errAsync (line 255) | function errAsync<T = never, E = unknown>(err: E): ResultAsync<T, E> {
type CombineResultAsyncs (line 265) | type CombineResultAsyncs<
type CombineResultsWithAllErrorsArrayAsync (line 272) | type CombineResultsWithAllErrorsArrayAsync<
type UnwrapAsync (line 279) | type UnwrapAsync<T> = IsLiteralArray<T> extends 1
type TraverseAsync (line 300) | type TraverseAsync<T, Depth extends number = 5> = IsLiteralArray<T> exte...
type TraverseWithAllErrorsAsync (line 330) | type TraverseWithAllErrorsAsync<T, Depth extends number = 5> = TraverseA...
type Writable (line 338) | type Writable<T> = T extends ReadonlyArray<unknown> ? [...T] : T
FILE: src/result.ts
function fromThrowable (line 23) | function fromThrowable<Fn extends (...args: readonly any[]) => any, E>(
function combine (line 43) | function combine<
function combineWithAllErrors (line 55) | function combineWithAllErrors<T extends readonly Result<unknown, unknown...
type Result (line 62) | type Result<T, E> = Ok<T, E> | Err<T, E>
function ok (line 66) | function ok<T, E = never>(value: T): Ok<T, E> {
function err (line 73) | function err<T = never, E = unknown>(err: E): Err<T, E> {
function safeTry (line 122) | function safeTry<T, E>(
type IResult (line 134) | interface IResult<T, E> {
class Ok (line 312) | class Ok<T, E> implements IResult<T, E> {
method constructor (line 313) | constructor(readonly value: T) {}
method isOk (line 315) | isOk(): this is Ok<T, E> {
method isErr (line 319) | isErr(): this is Err<T, E> {
method map (line 323) | map<A>(f: (t: T) => A): Result<A, E> {
method mapErr (line 328) | mapErr<U>(_f: (e: E) => U): Result<T, U> {
method andThen (line 337) | andThen(f: any): any {
method andThrough (line 344) | andThrough(f: any): any {
method andTee (line 348) | andTee(f: (t: T) => unknown): Result<T, E> {
method orTee (line 357) | orTee(_f: (t: E) => unknown): Result<T, E> {
method orElse (line 366) | orElse(_f: any): any {
method asyncAndThen (line 370) | asyncAndThen<U, F>(f: (t: T) => ResultAsync<U, F>): ResultAsync<U, E |...
method asyncAndThrough (line 379) | asyncAndThrough(f: (t: T) => ResultAsync<unknown, unknown>): any {
method asyncMap (line 383) | asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E> {
method unwrapOr (line 388) | unwrapOr<A>(_v: A): T | A {
method match (line 393) | match<A, B = A>(ok: (t: T) => A, _err: (e: E) => B): A | B {
method safeUnwrap (line 397) | safeUnwrap(): Generator<Err<never, E>, T> {
method _unsafeUnwrap (line 405) | _unsafeUnwrap(_?: ErrorConfig): T {
method _unsafeUnwrapErr (line 409) | _unsafeUnwrapErr(config?: ErrorConfig): E {
method [Symbol.iterator] (line 414) | *[Symbol.iterator](): Generator<Err<never, E>, T> {
class Err (line 419) | class Err<T, E> implements IResult<T, E> {
method constructor (line 420) | constructor(readonly error: E) {}
method isOk (line 422) | isOk(): this is Ok<T, E> {
method isErr (line 426) | isErr(): this is Err<T, E> {
method map (line 431) | map<A>(_f: (t: T) => A): Result<A, E> {
method mapErr (line 435) | mapErr<U>(f: (e: E) => U): Result<T, U> {
method andThrough (line 439) | andThrough<F>(_f: (t: T) => Result<unknown, F>): Result<T, E | F> {
method andTee (line 443) | andTee(_f: (t: T) => unknown): Result<T, E> {
method orTee (line 447) | orTee(f: (t: E) => unknown): Result<T, E> {
method andThen (line 461) | andThen(_f: any): any {
method orElse (line 470) | orElse(f: any): any {
method asyncAndThen (line 475) | asyncAndThen<U, F>(_f: (t: T) => ResultAsync<U, F>): ResultAsync<U, E ...
method asyncAndThrough (line 479) | asyncAndThrough<F>(_f: (t: T) => ResultAsync<unknown, F>): ResultAsync...
method asyncMap (line 484) | asyncMap<U>(_f: (t: T) => Promise<U>): ResultAsync<U, E> {
method unwrapOr (line 488) | unwrapOr<A>(v: A): T | A {
method match (line 492) | match<A, B = A>(_ok: (t: T) => A, err: (e: E) => B): A | B {
method safeUnwrap (line 496) | safeUnwrap(): Generator<Err<never, E>, T> {
method _unsafeUnwrap (line 505) | _unsafeUnwrap(config?: ErrorConfig): T {
method _unsafeUnwrapErr (line 509) | _unsafeUnwrapErr(_?: ErrorConfig): E {
method [Symbol.iterator] (line 513) | *[Symbol.iterator](): Generator<Err<never, E>, T> {
type Prev (line 530) | type Prev = [
type CollectResults (line 590) | type CollectResults<T, Collected extends unknown[] = [], Depth extends n...
type Transpose (line 614) | type Transpose<
type Combine (line 635) | type Combine<T, Depth extends number = 5> = Transpose<CollectResults<T>,...
type Dedup (line 645) | type Dedup<T> = T extends Result<infer RL, infer RR>
type MemberListOf (line 652) | type MemberListOf<T> = (
type EmptyArrayToNever (line 669) | type EmptyArrayToNever<T, NeverArrayToNever extends number = 0> = T exte...
type UnknownMembersToNever (line 680) | type UnknownMembersToNever<T> = T extends [infer H, ...infer R]
type MembersToUnion (line 685) | type MembersToUnion<T> = T extends unknown[] ? T[number] : never
type IsLiteralArray (line 688) | type IsLiteralArray<T> = T extends { length: infer L }
type Traverse (line 698) | type Traverse<T, Depth extends number = 5> = Combine<T, Depth> extends [...
type TraverseWithAllErrors (line 704) | type TraverseWithAllErrors<T, Depth extends number = 5> = Traverse<T, De...
type CombineResults (line 712) | type CombineResults<
type CombineResultsWithAllErrorsArray (line 719) | type CombineResultsWithAllErrorsArray<
FILE: tests/index.test.ts
type MessageObject (line 520) | type MessageObject = { message: string }
type HeterogenousList (line 566) | type HeterogenousList = [
type ExpecteResult (line 574) | type ExpecteResult = Result<[string, number, boolean], string | number |...
type HomogenousList (line 582) | type HomogenousList = [Result<string[], boolean>, Result<number[], string>]
type ExpectedResult (line 586) | type ExpectedResult = Result<[string[], number[]], boolean | string>
type HeterogenousList (line 626) | type HeterogenousList = [
type ExpecteResult (line 640) | type ExpecteResult = Result<[string, number, boolean, number[]], string ...
type HeterogenousList (line 674) | type HeterogenousList = [
type ExpecteResult (line 682) | type ExpecteResult = Result<[string, number, boolean], (string | number ...
type HomogenousList (line 690) | type HomogenousList = [Result<string[], boolean>, Result<number[], string>]
type ExpectedResult (line 694) | type ExpectedResult = Result<[string[], number[]], (boolean | string)[]>
type HeterogenousList (line 729) | type HeterogenousList = [
type ExpecteResult (line 737) | type ExpecteResult = Result<[string, number, boolean], (string | number ...
type ITestInterface (line 746) | interface ITestInterface {
FILE: tests/safe-try.test.ts
function good (line 122) | function good(): Result<number, string> {
function bad (line 125) | function bad(): Result<number, string> {
function promiseGood (line 128) | function promiseGood(): Promise<Result<number, string>> {
function promiseBad (line 131) | function promiseBad(): Promise<Result<number, string>> {
function asyncGood (line 134) | function asyncGood(): ResultAsync<number, string> {
function asyncBad (line 137) | function asyncBad(): ResultAsync<number, string> {
function myFunc (line 142) | function myFunc(): Result<number, string> {
function myFunc (line 162) | function myFunc(): Result<number, string> {
function myFunc (line 182) | function myFunc(): ResultAsync<number, string> {
function myFunc (line 202) | function myFunc(): ResultAsync<number, string> {
function myFunc (line 222) | function myFunc(): ResultAsync<number, string> {
function good (line 270) | function good(): Result<number, string> {
function bad (line 273) | function bad(): Result<number, string> {
function promiseGood (line 276) | function promiseGood(): Promise<Result<number, string>> {
function promiseBad (line 279) | function promiseBad(): Promise<Result<number, string>> {
function asyncGood (line 282) | function asyncGood(): ResultAsync<number, string> {
function asyncBad (line 285) | function asyncBad(): ResultAsync<number, string> {
function fn (line 290) | function fn(): Result<number, string> {
function myFunc (line 305) | function myFunc(): Result<number, string> {
function myFunc (line 319) | function myFunc(): ResultAsync<number, string> {
function myFunc (line 333) | function myFunc(): ResultAsync<number, string> {
function myFunc (line 349) | function myFunc(): ResultAsync<number, string> {
FILE: tests/typecheck-tests.ts
type CreateTuple (line 19) | type CreateTuple<L, V = string> =
type Expectation (line 39) | type Expectation = Result<unknown, string>
type MyError (line 46) | interface MyError {
type Expectation (line 51) | type Expectation = Result<string, MyError>
type MyError (line 58) | interface MyError {
type Expectation (line 63) | type Expectation = Result<string, MyError | string[]>
type Expectation (line 70) | type Expectation = Result<unknown, string | number | boolean>
type MyError (line 86) | interface MyError {
type Expectation (line 90) | type Expectation = Result<unknown, string | number | MyError>
type Expectation (line 106) | type Expectation = Result<number, unknown>
type Expectation (line 121) | type Expectation = Result<string, unknown>
type MyError (line 135) | interface MyError {
type Expectation (line 139) | type Expectation = Result<unknown, string | number | MyError>
type Expectation (line 155) | type Expectation = Result<'yo', number>
type Expectation (line 165) | type Expectation = Result<number, string>
type MyError (line 172) | interface MyError {
type Expectation (line 177) | type Expectation = Result<number, MyError>
type MyError (line 184) | interface MyError {
type Expectation (line 189) | type Expectation = Result<number, MyError | string[]>
type Expectation (line 196) | type Expectation = Result<number, string | number | boolean>
type MyError (line 212) | interface MyError {
type Expectation (line 216) | type Expectation = Result<number, string | number | MyError>
type Expectation (line 232) | type Expectation = Result<number, unknown>
type Expectation (line 247) | type Expectation = Result<number, unknown>
type MyError (line 261) | interface MyError {
type Expectation (line 265) | type Expectation = Result<number, string | number | MyError>
type Expectation (line 281) | type Expectation = Result<number, string>
type Expectation (line 291) | type Expectation = string
type Expectation (line 306) | type Expectation = Result<number, number>
type Expectation (line 320) | type Expectation = Result<number, number | string>
type Expectation (line 334) | type Expectation = Result<number, number | string>
type Expectation (line 350) | type Expectation = Result<'yo', string>
type Expectation (line 358) | type Expectation = Result<string | number, boolean>
type Expectation (line 365) | type Expectation = Result<string | number | boolean, unknown>
type Expectation (line 382) | type Expectation = Result<string | true, false>
type OKExpectation (line 398) | type OKExpectation = number
type ErrExpectation (line 399) | type ErrExpectation = string
type Expectation (line 414) | type Expectation = boolean
type Expectation (line 429) | type Expectation = boolean | bigint
type Expectation (line 446) | type Expectation = ResultAsync<unknown, string>
type MyError (line 453) | interface MyError {
type Expectation (line 458) | type Expectation = ResultAsync<string, MyError>
type MyError (line 465) | interface MyError {
type Expectation (line 470) | type Expectation = ResultAsync<string, MyError | string[]>
type Expectation (line 479) | type Expectation = ResultAsync<unknown, string>
type MyError (line 486) | interface MyError {
type Expectation (line 491) | type Expectation = ResultAsync<number, MyError>
type MyError (line 498) | interface MyError {
type Expectation (line 503) | type Expectation = ResultAsync<number, MyError | string[]>
type Expectation (line 510) | type Expectation = ResultAsync<number, string | number | boolean>
type MyError (line 526) | interface MyError {
type Expectation (line 530) | type Expectation = Result<number, string | number | MyError>
type Expectation (line 546) | type Expectation = Result<number, unknown>
type Expectation (line 561) | type Expectation = Result<number, unknown>
type MyError (line 575) | interface MyError {
type Expectation (line 579) | type Expectation = Result<number, string | number | MyError>
type Expectation (line 595) | type Expectation = Result<number, string>
type MyError (line 604) | interface MyError {
type Expectation (line 608) | type Expectation = ResultAsync<number, string | number | MyError>
type Expectation (line 628) | type Expectation = Result<[ number, string, boolean, boolean ], Error | ...
type Expectation (line 642) | type Expectation = Result<[ number, string ], never>;
type Expectation (line 654) | type Expectation = Result<[ never, never ], number | 'abc'>;
type Expectation (line 666) | type Expectation = Result<never, never>;
type Expectation (line 676) | type Expectation = Result<string[], string>;
type Input (line 687) | type Input = CreateTuple<6, Result<string, never>>
type Expectation (line 688) | type Expectation = Result<CreateTuple<6, string>, never>
type Input (line 699) | type Input = CreateTuple<7, Result<string, never>>
type Expectation (line 700) | type Expectation = Result<CreateTuple<7, string>, never>
type Input (line 711) | type Input = CreateTuple<8, Result<string, never>>
type Expectation (line 712) | type Expectation = Result<CreateTuple<8, string>, never>
type Input (line 723) | type Input = CreateTuple<9, Result<string, never>>
type Expectation (line 724) | type Expectation = Result<CreateTuple<9, string>, never>
type Input (line 735) | type Input = CreateTuple<10, Result<string, never>>
type Expectation (line 736) | type Expectation = Result<CreateTuple<10, string>, never>
type Input (line 747) | type Input = CreateTuple<11, Result<string, never>>
type Expectation (line 748) | type Expectation = Result<CreateTuple<11, string>, never>
type Input (line 759) | type Input = CreateTuple<12, Result<string, never>>
type Expectation (line 760) | type Expectation = Result<CreateTuple<12, string>, never>
type Input (line 771) | type Input = CreateTuple<13, Result<string, never>>
type Expectation (line 772) | type Expectation = Result<CreateTuple<13, string>, never>
type Input (line 783) | type Input = CreateTuple<14, Result<string, never>>
type Expectation (line 784) | type Expectation = Result<CreateTuple<14, string>, never>
type Input (line 795) | type Input = CreateTuple<15, Result<string, never>>
type Expectation (line 796) | type Expectation = Result<CreateTuple<15, string>, never>
type Input (line 807) | type Input = CreateTuple<16, Result<string, never>>
type Expectation (line 808) | type Expectation = Result<CreateTuple<16, string>, never>
type Input (line 819) | type Input = CreateTuple<17, Result<string, never>>
type Expectation (line 820) | type Expectation = Result<CreateTuple<17, string>, never>
type Input (line 831) | type Input = CreateTuple<18, Result<string, never>>
type Expectation (line 832) | type Expectation = Result<CreateTuple<18, string>, never>
type Input (line 843) | type Input = CreateTuple<19, Result<string, never>>
type Expectation (line 844) | type Expectation = Result<CreateTuple<19, string>, never>
type Input (line 855) | type Input = CreateTuple<20, Result<string, never>>
type Expectation (line 856) | type Expectation = Result<CreateTuple<20, string>, never>
type Input (line 867) | type Input = CreateTuple<21, Result<string, never>>
type Expectation (line 868) | type Expectation = Result<CreateTuple<21, string>, never>
type Input (line 879) | type Input = CreateTuple<22, Result<string, never>>
type Expectation (line 880) | type Expectation = Result<CreateTuple<22, string>, never>
type Input (line 891) | type Input = CreateTuple<23, Result<string, never>>
type Expectation (line 892) | type Expectation = Result<CreateTuple<23, string>, never>
type Input (line 903) | type Input = CreateTuple<24, Result<string, never>>
type Expectation (line 904) | type Expectation = Result<CreateTuple<24, string>, never>
type Input (line 915) | type Input = CreateTuple<25, Result<string, never>>
type Expectation (line 916) | type Expectation = Result<CreateTuple<25, string>, never>
type Input (line 927) | type Input = CreateTuple<26, Result<string, never>>
type Expectation (line 928) | type Expectation = Result<CreateTuple<26, string>, never>
type Input (line 939) | type Input = CreateTuple<27, Result<string, never>>
type Expectation (line 940) | type Expectation = Result<CreateTuple<27, string>, never>
type Input (line 951) | type Input = CreateTuple<28, Result<string, never>>
type Expectation (line 952) | type Expectation = Result<CreateTuple<28, string>, never>
type Input (line 963) | type Input = CreateTuple<29, Result<string, never>>
type Expectation (line 964) | type Expectation = Result<CreateTuple<29, string>, never>
type Input (line 975) | type Input = CreateTuple<30, Result<string, never>>
type Expectation (line 976) | type Expectation = Result<CreateTuple<30, string>, never>
type Input (line 987) | type Input = CreateTuple<31, Result<string, never>>
type Expectation (line 988) | type Expectation = Result<CreateTuple<31, string>, never>
type Input (line 999) | type Input = CreateTuple<32, Result<string, never>>
type Expectation (line 1000) | type Expectation = Result<CreateTuple<32, string>, never>
type Input (line 1011) | type Input = CreateTuple<33, Result<string, never>>
type Expectation (line 1012) | type Expectation = Result<CreateTuple<33, string>, never>
type Input (line 1023) | type Input = CreateTuple<34, Result<string, never>>
type Expectation (line 1024) | type Expectation = Result<CreateTuple<34, string>, never>
type Input (line 1035) | type Input = CreateTuple<35, Result<string, never>>
type Expectation (line 1036) | type Expectation = Result<CreateTuple<35, string>, never>
type Input (line 1047) | type Input = CreateTuple<36, Result<string, never>>
type Expectation (line 1048) | type Expectation = Result<CreateTuple<36, string>, never>
type Input (line 1059) | type Input = CreateTuple<37, Result<string, never>>
type Expectation (line 1060) | type Expectation = Result<CreateTuple<37, string>, never>
type Input (line 1071) | type Input = CreateTuple<38, Result<string, never>>
type Expectation (line 1072) | type Expectation = Result<CreateTuple<38, string>, never>
type Input (line 1083) | type Input = CreateTuple<39, Result<string, never>>
type Expectation (line 1084) | type Expectation = Result<CreateTuple<39, string>, never>
type Input (line 1095) | type Input = CreateTuple<40, Result<string, never>>
type Expectation (line 1096) | type Expectation = Result<CreateTuple<40, string>, never>
type Input (line 1107) | type Input = CreateTuple<41, Result<string, never>>
type Expectation (line 1108) | type Expectation = Result<CreateTuple<41, string>, never>
type Input (line 1119) | type Input = CreateTuple<42, Result<string, never>>
type Expectation (line 1120) | type Expectation = Result<CreateTuple<42, string>, never>
type Input (line 1131) | type Input = CreateTuple<43, Result<string, never>>
type Expectation (line 1132) | type Expectation = Result<CreateTuple<43, string>, never>
type Input (line 1143) | type Input = CreateTuple<44, Result<string, never>>
type Expectation (line 1144) | type Expectation = Result<CreateTuple<44, string>, never>
type Input (line 1155) | type Input = CreateTuple<45, Result<string, never>>
type Expectation (line 1156) | type Expectation = Result<CreateTuple<45, string>, never>
type Input (line 1167) | type Input = CreateTuple<46, Result<string, never>>
type Expectation (line 1168) | type Expectation = Result<CreateTuple<46, string>, never>
type Input (line 1179) | type Input = CreateTuple<47, Result<string, never>>
type Expectation (line 1180) | type Expectation = Result<CreateTuple<47, string>, never>
type Input (line 1191) | type Input = CreateTuple<48, Result<string, never>>
type Expectation (line 1192) | type Expectation = Result<CreateTuple<48, string>, never>
type Input (line 1203) | type Input = CreateTuple<49, Result<string, never>>
type Expectation (line 1204) | type Expectation = Result<CreateTuple<49, string>, never>
type Expectation (line 1218) | type Expectation = Result<[ number, string, never, never ], (string[] | ...
type Expectation (line 1232) | type Expectation = Result<[ number, string ], never[]>;
type Expectation (line 1244) | type Expectation = Result<[ never, never ], (number | 'string')[]>;
type Expectation (line 1256) | type Expectation = Result<string[], (number | string)[]>;
type Expectation (line 1266) | type Expectation = Result<(string | boolean)[], (number | string)[]>;
type Input (line 1277) | type Input = CreateTuple<6, Result<string, number>>
type Expectation (line 1278) | type Expectation = Result<CreateTuple<6, string>, number[]>
type Input (line 1289) | type Input = CreateTuple<15, Result<string, number>>
type Expectation (line 1290) | type Expectation = Result<CreateTuple<15, string>, number[]>
type Input (line 1301) | type Input = CreateTuple<30, Result<string, number>>
type Expectation (line 1302) | type Expectation = Result<CreateTuple<30, string>, number[]>
type Input (line 1313) | type Input = CreateTuple<49 , Result<string, number>>
type Expectation (line 1314) | type Expectation = Result<CreateTuple<49, string>, number[]>
type Expectation (line 1328) | type Expectation = Result<never, 'error'>
type Expectation (line 1336) | type Expectation = Result<never, { abc: number }>
type Expectation (line 1349) | type Expectation = ResultAsync<unknown, string>
type MyError (line 1356) | interface MyError {
type Expectation (line 1361) | type Expectation = ResultAsync<string, MyError>
type MyError (line 1368) | interface MyError {
type Expectation (line 1373) | type Expectation = ResultAsync<string, MyError | string[]>
type Expectation (line 1381) | type Expectation = ResultAsync<unknown, string | number | boolean>
type MyError (line 1397) | interface MyError {
type Expectation (line 1401) | type Expectation = ResultAsync<unknown, string | number | MyError>
type Expectation (line 1417) | type Expectation = ResultAsync<number, unknown>
type Expectation (line 1432) | type Expectation = ResultAsync<string, unknown>
type MyError (line 1446) | interface MyError {
type Expectation (line 1450) | type Expectation = ResultAsync<unknown, string | number | MyError>
type Expectation (line 1468) | type Expectation = ResultAsync<unknown, string | number | boolean>
type MyError (line 1484) | interface MyError {
type Expectation (line 1488) | type Expectation = ResultAsync<unknown, string | number | MyError>
type Expectation (line 1504) | type Expectation = ResultAsync<number, unknown>
type Expectation (line 1519) | type Expectation = ResultAsync<string, unknown>
type MyError (line 1533) | interface MyError {
type Expectation (line 1537) | type Expectation = ResultAsync<unknown, string | number | MyError>
type Expectation (line 1555) | type Expectation = ResultAsync<number | boolean, string | number | boolean>
type Expectation (line 1573) | type Expectation = ResultAsync<number, 'impossible error'>
type Expectation (line 1583) | type Expectation = string
type Expectation (line 1598) | type Expectation = ResultAsync<number, number>
type Expectation (line 1612) | type Expectation = ResultAsync<number, number | string>
type Expectation (line 1626) | type Expectation = ResultAsync<number, number | string>
type Expectation (line 1642) | type Expectation = ResultAsync<number, number | string>
type Expectation (line 1658) | type Expectation = ResultAsync<string | number, boolean>
type Expectation (line 1665) | type Expectation = ResultAsync<string | number | boolean, unknown>
type Expectation (line 1682) | type Expectation = ResultAsync<string | true, false>
type Expectation (line 1698) | type Expectation = ResultAsync<[ number, string, boolean, boolean ], Err...
type Expectation (line 1712) | type Expectation = ResultAsync<[ number, string ], never>;
type Expectation (line 1724) | type Expectation = ResultAsync<[ never, never ], number | string>;
type Expectation (line 1736) | type Expectation = ResultAsync<never, never>;
type Expectation (line 1746) | type Expectation = ResultAsync<string[], string>;
type Input (line 1757) | type Input = CreateTuple<6, ResultAsync<string, never>>
type Expectation (line 1758) | type Expectation = ResultAsync<CreateTuple<6, string>, never>
type Input (line 1769) | type Input = CreateTuple<7, ResultAsync<string, never>>
type Expectation (line 1770) | type Expectation = ResultAsync<CreateTuple<7, string>, never>
type Input (line 1781) | type Input = CreateTuple<8, ResultAsync<string, never>>
type Expectation (line 1782) | type Expectation = ResultAsync<CreateTuple<8, string>, never>
type Input (line 1793) | type Input = CreateTuple<9, ResultAsync<string, never>>
type Expectation (line 1794) | type Expectation = ResultAsync<CreateTuple<9, string>, never>
type Input (line 1805) | type Input = CreateTuple<10, ResultAsync<string, never>>
type Expectation (line 1806) | type Expectation = ResultAsync<CreateTuple<10, string>, never>
type Input (line 1817) | type Input = CreateTuple<11, ResultAsync<string, never>>
type Expectation (line 1818) | type Expectation = ResultAsync<CreateTuple<11, string>, never>
type Input (line 1829) | type Input = CreateTuple<12, ResultAsync<string, never>>
type Expectation (line 1830) | type Expectation = ResultAsync<CreateTuple<12, string>, never>
type Input (line 1841) | type Input = CreateTuple<13, ResultAsync<string, never>>
type Expectation (line 1842) | type Expectation = ResultAsync<CreateTuple<13, string>, never>
type Input (line 1853) | type Input = CreateTuple<14, ResultAsync<string, never>>
type Expectation (line 1854) | type Expectation = ResultAsync<CreateTuple<14, string>, never>
type Input (line 1865) | type Input = CreateTuple<15, ResultAsync<string, never>>
type Expectation (line 1866) | type Expectation = ResultAsync<CreateTuple<15, string>, never>
type Input (line 1877) | type Input = CreateTuple<16, ResultAsync<string, never>>
type Expectation (line 1878) | type Expectation = ResultAsync<CreateTuple<16, string>, never>
type Input (line 1889) | type Input = CreateTuple<17, ResultAsync<string, never>>
type Expectation (line 1890) | type Expectation = ResultAsync<CreateTuple<17, string>, never>
type Input (line 1901) | type Input = CreateTuple<18, ResultAsync<string, never>>
type Expectation (line 1902) | type Expectation = ResultAsync<CreateTuple<18, string>, never>
type Input (line 1913) | type Input = CreateTuple<19, ResultAsync<string, never>>
type Expectation (line 1914) | type Expectation = ResultAsync<CreateTuple<19, string>, never>
type Input (line 1925) | type Input = CreateTuple<20, ResultAsync<string, never>>
type Expectation (line 1926) | type Expectation = ResultAsync<CreateTuple<20, string>, never>
type Input (line 1937) | type Input = CreateTuple<21, ResultAsync<string, never>>
type Expectation (line 1938) | type Expectation = ResultAsync<CreateTuple<21, string>, never>
type Input (line 1949) | type Input = CreateTuple<22, ResultAsync<string, never>>
type Expectation (line 1950) | type Expectation = ResultAsync<CreateTuple<22, string>, never>
type Input (line 1961) | type Input = CreateTuple<23, ResultAsync<string, never>>
type Expectation (line 1962) | type Expectation = ResultAsync<CreateTuple<23, string>, never>
type Input (line 1973) | type Input = CreateTuple<24, ResultAsync<string, never>>
type Expectation (line 1974) | type Expectation = ResultAsync<CreateTuple<24, string>, never>
type Input (line 1985) | type Input = CreateTuple<25, ResultAsync<string, never>>
type Expectation (line 1986) | type Expectation = ResultAsync<CreateTuple<25, string>, never>
type Input (line 1997) | type Input = CreateTuple<26, ResultAsync<string, never>>
type Expectation (line 1998) | type Expectation = ResultAsync<CreateTuple<26, string>, never>
type Input (line 2009) | type Input = CreateTuple<27, ResultAsync<string, never>>
type Expectation (line 2010) | type Expectation = ResultAsync<CreateTuple<27, string>, never>
type Input (line 2021) | type Input = CreateTuple<28, ResultAsync<string, never>>
type Expectation (line 2022) | type Expectation = ResultAsync<CreateTuple<28, string>, never>
type Input (line 2033) | type Input = CreateTuple<29, ResultAsync<string, never>>
type Expectation (line 2034) | type Expectation = ResultAsync<CreateTuple<29, string>, never>
type Input (line 2045) | type Input = CreateTuple<30, ResultAsync<string, never>>
type Expectation (line 2046) | type Expectation = ResultAsync<CreateTuple<30, string>, never>
type Input (line 2057) | type Input = CreateTuple<31, ResultAsync<string, never>>
type Expectation (line 2058) | type Expectation = ResultAsync<CreateTuple<31, string>, never>
type Input (line 2069) | type Input = CreateTuple<32, ResultAsync<string, never>>
type Expectation (line 2070) | type Expectation = ResultAsync<CreateTuple<32, string>, never>
type Input (line 2081) | type Input = CreateTuple<33, ResultAsync<string, never>>
type Expectation (line 2082) | type Expectation = ResultAsync<CreateTuple<33, string>, never>
type Input (line 2093) | type Input = CreateTuple<34, ResultAsync<string, never>>
type Expectation (line 2094) | type Expectation = ResultAsync<CreateTuple<34, string>, never>
type Input (line 2105) | type Input = CreateTuple<35, ResultAsync<string, never>>
type Expectation (line 2106) | type Expectation = ResultAsync<CreateTuple<35, string>, never>
type Input (line 2117) | type Input = CreateTuple<36, ResultAsync<string, never>>
type Expectation (line 2118) | type Expectation = ResultAsync<CreateTuple<36, string>, never>
type Input (line 2129) | type Input = CreateTuple<37, ResultAsync<string, never>>
type Expectation (line 2130) | type Expectation = ResultAsync<CreateTuple<37, string>, never>
type Input (line 2141) | type Input = CreateTuple<38, ResultAsync<string, never>>
type Expectation (line 2142) | type Expectation = ResultAsync<CreateTuple<38, string>, never>
type Input (line 2153) | type Input = CreateTuple<39, ResultAsync<string, never>>
type Expectation (line 2154) | type Expectation = ResultAsync<CreateTuple<39, string>, never>
type Input (line 2165) | type Input = CreateTuple<40, ResultAsync<string, never>>
type Expectation (line 2166) | type Expectation = ResultAsync<CreateTuple<40, string>, never>
type Input (line 2177) | type Input = CreateTuple<41, ResultAsync<string, never>>
type Expectation (line 2178) | type Expectation = ResultAsync<CreateTuple<41, string>, never>
type Input (line 2189) | type Input = CreateTuple<42, ResultAsync<string, never>>
type Expectation (line 2190) | type Expectation = ResultAsync<CreateTuple<42, string>, never>
type Input (line 2201) | type Input = CreateTuple<43, ResultAsync<string, never>>
type Expectation (line 2202) | type Expectation = ResultAsync<CreateTuple<43, string>, never>
type Input (line 2213) | type Input = CreateTuple<44, ResultAsync<string, never>>
type Expectation (line 2214) | type Expectation = ResultAsync<CreateTuple<44, string>, never>
type Input (line 2225) | type Input = CreateTuple<45, ResultAsync<string, never>>
type Expectation (line 2226) | type Expectation = ResultAsync<CreateTuple<45, string>, never>
type Input (line 2237) | type Input = CreateTuple<46, ResultAsync<string, never>>
type Expectation (line 2238) | type Expectation = ResultAsync<CreateTuple<46, string>, never>
type Input (line 2249) | type Input = CreateTuple<47, ResultAsync<string, never>>
type Expectation (line 2250) | type Expectation = ResultAsync<CreateTuple<47, string>, never>
type Input (line 2261) | type Input = CreateTuple<48, ResultAsync<string, never>>
type Expectation (line 2262) | type Expectation = ResultAsync<CreateTuple<48, string>, never>
type Input (line 2273) | type Input = CreateTuple<49, ResultAsync<string, never>>
type Expectation (line 2274) | type Expectation = ResultAsync<CreateTuple<49, string>, never>
type Expectation (line 2288) | type Expectation = ResultAsync<[ number, string, never, never ], (string...
type Expectation (line 2302) | type Expectation = ResultAsync<[ number, string ], never[]>;
type Expectation (line 2314) | type Expectation = ResultAsync<[ never, never ], (number | string)[]>;
type Expectation (line 2326) | type Expectation = ResultAsync<string[], (number | string)[]>;
type Expectation (line 2336) | type Expectation = ResultAsync<(string | boolean)[], (number | string)[]>;
type Input (line 2347) | type Input = CreateTuple<6, ResultAsync<string, number>>
type Expectation (line 2348) | type Expectation = ResultAsync<CreateTuple<6, string>, number[]>
type Input (line 2359) | type Input = CreateTuple<15, ResultAsync<string, number>>
type Expectation (line 2360) | type Expectation = ResultAsync<CreateTuple<15, string>, number[]>
type Input (line 2371) | type Input = CreateTuple<30, ResultAsync<string, number>>
type Expectation (line 2372) | type Expectation = ResultAsync<CreateTuple<30, string>, number[]>
type Input (line 2383) | type Input = CreateTuple<49 , ResultAsync<string, number>>
type Expectation (line 2384) | type Expectation = ResultAsync<CreateTuple<49, string>, number[]>
type ReturnMyError (line 2404) | interface ReturnMyError {
type Expectation (line 2408) | type Expectation = Result<string, ReturnMyError>
type ReturnMyError (line 2419) | interface ReturnMyError {
type Expectation (line 2423) | type Expectation = Result<string, ReturnMyError>
type YieldMyError (line 2434) | interface YieldMyError {
type ReturnMyError (line 2437) | interface ReturnMyError {
type Expectation (line 2442) | type Expectation = number
type FirstYieldMyError (line 2454) | interface FirstYieldMyError {
type SecondYieldMyError (line 2457) | interface SecondYieldMyError {
type ReturnMyError (line 2460) | interface ReturnMyError {
type Expectation (line 2464) | type Expectation = Result<string, FirstYieldMyError | SecondYieldMyError...
type ReturnMyError (line 2479) | interface ReturnMyError {
type Expectation (line 2483) | type Expectation = ResultAsync<string, ReturnMyError>
type ReturnMyError (line 2494) | interface ReturnMyError {
type Expectation (line 2498) | type Expectation = ResultAsync<string, ReturnMyError>
type YieldMyError (line 2509) | interface YieldMyError {
type ReturnMyError (line 2512) | interface ReturnMyError {
type Expectation (line 2517) | type Expectation = number
type FirstYieldMyError (line 2529) | interface FirstYieldMyError {
type SecondYieldMyError (line 2532) | interface SecondYieldMyError {
type ReturnMyError (line 2535) | interface ReturnMyError {
type Expectation (line 2539) | type Expectation = ResultAsync<string, FirstYieldMyError | SecondYieldMy...
type Expectation (line 2565) | type Expectation = [
type Expectation (line 2576) | type Expectation = []
type Expectation (line 2590) | type Expectation = [[1], [3]]
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (247K chars).
[
{
"path": ".babelrc",
"chars": 67,
"preview": "{\n \"presets\": [\"@babel/preset-env\", \"@babel/preset-typescript\"]\n}\n"
},
{
"path": ".changeset/config.json",
"chars": 337,
"preview": "{\n \"$schema\": \"https://unpkg.com/@changesets/config@3.0.0/schema.json\",\n \"changelog\": [\n \"@changesets/changelog-git"
},
{
"path": ".eslintrc.js",
"chars": 891,
"preview": "module.exports = {\n root: true,\n parser: '@typescript-eslint/parser',\n env: {\n node: true,\n },\n ignorePatterns: "
},
{
"path": ".github/workflows/ci.yml",
"chars": 3196,
"preview": "name: CI\non:\n pull_request:\n push:\n branches:\n - master\nconcurrency:\n group: ${{ github.workflow }}-${{ githu"
},
{
"path": ".github/workflows/release.yml",
"chars": 1187,
"preview": "name: Release\n\non:\n push:\n branches:\n - master\n\nconcurrency: ${{ github.workflow }}-${{ github.ref }}\n\njobs:\n "
},
{
"path": ".gitignore",
"chars": 35,
"preview": "dist/\ntmp/\nnode_modules/\npublish.sh"
},
{
"path": ".prettierrc.js",
"chars": 115,
"preview": "module.exports = {\n semi: false,\n trailingComma: \"all\",\n singleQuote: true,\n printWidth: 100,\n tabWidth: 2\n};\n"
},
{
"path": "CHANGELOG.md",
"chars": 4395,
"preview": "# neverthrow\n\n## 8.2.0\n\n### Minor Changes\n\n- [#615](https://github.com/supermacro/neverthrow/pull/615) [`85ed7fd`](https"
},
{
"path": "CODEOWNERS",
"chars": 23,
"preview": "* @m-shaka @supermacro\n"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "MIT License\n\nCopyright (c) 2019 Giorgio Delgado\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "README.md",
"chars": 50686,
"preview": "# NeverThrow 🙅\n\n[. The extraction includes 24 files (232.0 KB), approximately 60.2k tokens, and a symbol index with 462 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.