true-myth
- Version 9.3.1
- Published
- 793 kB
- No dependencies
- MIT license
Install
npm i true-mythyarn add true-mythpnpm add true-mythOverview
Index
Variables
Interfaces
Type Aliases
Namespaces
maybe
- and()
- andThen()
- AnyArray
- AnyMaybe
- ap()
- equals()
- find()
- first()
- flatten()
- fromJSON()
- get()
- inspect()
- isInstance()
- isJust()
- IsMaybe
- isNothing()
- just
- Just
- JustJSON
- last()
- map()
- mapOr()
- mapOrElse()
- match()
- Matcher
- Maybe
- Maybe
- MaybeConstructor
- MaybeJSON
- NarrowingPredicate
- nothing
- Nothing
- NothingJSON
- of
- or()
- orElse()
- Predicate
- property()
- ReadonlyArray
- safe()
- Serialized
- T
- toJSON()
- toString()
- transposeArray()
- TransposedArray
- unwrapOr()
- unwrapOrElse()
- Unwrapped
- Variant
- Variant
result
- []
- []
- []
- A
- A
- A
- all()
- and()
- andThen()
- AnyResult
- AnyResult
- AnyResult
- AnyResult
- ap()
- equals()
- err
- Err
- ErrJSON
- flatten()
- fromJSON()
- inspect()
- inspectErr()
- isErr()
- isInstance()
- isOk()
- IsResult
- map()
- mapErr()
- mapOr()
- mapOrElse()
- match()
- Matcher
- ok
- Ok
- OkJSON
- or()
- orElse()
- readonly
- readonly
- Result
- Result
- ResultConstructor
- ResultJSON
- safe()
- Serialized
- SomeResult
- toJSON()
- toString()
- transposeAll()
- transposeAny()
- tryOr()
- tryOrElse()
- unwrapOr()
- unwrapOrElse()
- Variant
- Variant
task
- []
- []
- []
- A
- A
- A
- AggregateRejection
- all()
- allSettled()
- and()
- andThen()
- any()
- AnyTask
- AnyTask
- AnyTask
- flatten()
- fromPromise()
- fromResult()
- fromUnsafePromise()
- inspect()
- inspectRejected()
- InvalidAccess
- isRetryFailed()
- IsTask
- map()
- mapRejected()
- match()
- Matcher
- or()
- orElse()
- Pending
- PhantomData
- race()
- readonly
- readonly
- readonly
- reject
- Rejected
- resolve
- Resolved
- RETRY_FAILED_NAME
- RetryFailed
- RetryStatus
- safe()
- safelyTry()
- safelyTryOr
- safelyTryOrElse
- safeNullable()
- State
- stopRetrying()
- StopRetrying
- Task
- Task
- TaskConstructor
- TaskExecutorException
- TaskTypesFor
- timeout()
- Timeout
- timer()
- Timer
- toPromise()
- tryOr()
- tryOrElse()
- UnsafePromise
- withResolvers()
- WithResolvers
- withRetries()
Variables
variable Maybe
const Maybe: MaybeConstructor;Mayberepresents a value which may () or may not () be present.
variable Result
const Result: ResultConstructor;A
Resultrepresents success () or failure ().The behavior of this type is checked by TypeScript at compile time, and bears no runtime overhead other than the very small cost of the container object.
variable Task
const Task: TaskConstructor;A
Taskis a type safe asynchronous computation.You can think of a
Task<T, E>as being basically aPromise<Result<T, E>>, because it *is* aPromise<Result<T, E>>under the hood, but with two main differences from a “normal”Promise:1. A
Task*cannot* “reject”. All errors must be handled. This means that, like a , it will *never* throw an error if used in strict TypeScript.2. Unlike
Promise,Taskrobustly distinguishes betweenmapandandThenoperations.Taskalso implements JavaScript’sPromiseLikeinterface, so you canawaitit; when aTask<T, E>is awaited, it produces a .
variable Unit
const Unit: Unit;The
Unittype exists for the cases where you want a type-safe equivalent ofundefinedornull. It's a concrete instance, which won't blow up on you, and you can safely use it with e.g.Resultwithout being concerned that you'll accidentally introducenullorundefinedback into your application.Equivalent to
()or "unit" in many functional or functional-influenced languages.
Interfaces
interface Unit
interface Unit extends _Unit {}Type Aliases
type Maybe
type Maybe<T extends {}> = Just<T> | Nothing<T>;Mayberepresents a value which may () or may not () be present.
type Result
type Result<T, E> = Ok<T, E> | Err<T, E>;A
Resultrepresents success () or failure ().The behavior of this type is checked by TypeScript at compile time, and bears no runtime overhead other than the very small cost of the container object.
type Task
type Task<T, E> = Pending<T, E> | Resolved<T, E> | Rejected<T, E>;A
Taskis a type safe asynchronous computation.You can think of a
Task<T, E>as being basically aPromise<Result<T, E>>, because it *is* aPromise<Result<T, E>>under the hood, but with two main differences from a “normal”Promise:1. A
Task*cannot* “reject”. All errors must be handled. This means that, like a , it will *never* throw an error if used in strict TypeScript.2. Unlike
Promise,Taskrobustly distinguishes betweenmapandandThenoperations.Taskalso implements JavaScript’sPromiseLikeinterface, so you canawaitit; when aTask<T, E>is awaited, it produces a .T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
Namespaces
namespace maybe
module 'dist/maybe.d.ts' {}A value of type
Twhich may, or may not, be present. If the value is present, it is . If it's absent, it is instead.For a deep dive on the type, see [the guide](/guide/understanding/maybe.md).
variable IsMaybe
const IsMaybe: Symbol;variable just
const just: { <F extends (...args: any) => {}>(value: F): Maybe<F>; <T extends {}, F extends (...args: any) => T>(value: F): never; <F extends (...args: any) => null>(value: F): never; <T extends {}>(value: T): Maybe<T>;};Create a instance which is a .
nullandundefinedare allowed by the type signature so that the function maythrowon those rather than constructing a type likeMaybe<undefined>.T The type of the item contained in the
Maybe.Parameter value
The value to wrap in a
Maybe.Just.Returns
An instance of
Maybe.Just<T>.Throws
If you pass
nullorundefined.
variable Maybe
const Maybe: MaybeConstructor;Mayberepresents a value which may () or may not () be present.
variable nothing
const nothing: <T extends {}>(_?: null) => Nothing<T>;Create a instance which is a .
If you want to create an instance with a specific type, e.g. for use in a function which expects a
Maybe<T>where the<T>is known but you have no value to give it, you can use a type parameter:```ts const notString = Maybe.nothing(); ```
T The type of the item contained in the
Maybe.Returns
An instance of
Maybe.Nothing<T>.
variable of
const of: { <F extends (...args: any) => {}>(value: F): Maybe<F>; <T extends {}, F extends (...args: any) => T>(value: F): never; <F extends (...args: any) => null>(value: F): never; <T extends {}>(value: T): Maybe<T>; (value: unknown): Maybe<{}>;};Create a from any value.
To specify that the result should be interpreted as a specific type, you may invoke
Maybe.ofwith an explicit type parameter:```ts import * as maybe from 'true-myth/maybe'; const foo = maybe.of(null); ```
This is usually only important in two cases:
1. If you are intentionally constructing a
Nothingfrom a knownnullor undefined value *which is untyped*. 2. If you are specifying that the type is more general than the value passed (since TypeScript can define types as literals).T The type of the item contained in the
Maybe.Parameter value
The value to wrap in a
Maybe. If it isundefinedornull, the result will beNothing; otherwise it will be the type of the value passed.
variable ReadonlyArray
const ReadonlyArray: any;variable T
const T: any;variable Variant
const Variant: { readonly Just: 'Just'; readonly Nothing: 'Nothing' };Discriminant for the and type instances.
You can use the discriminant via the
variantproperty of instances if you need to match explicitly on it.
function and
and: { <T extends {}, U extends {}>(andMaybe: Maybe<U>, maybe: Maybe<T>): Maybe<U>; <T extends {}, U extends {}>(andMaybe: Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;};You can think of this like a short-circuiting logical "and" operation on a type. If
maybeis , then the result is theandMaybe. Ifmaybeis , the result isNothing.This is useful when you have another
Maybevalue you want to provide if and only if* you have aJust– that is, when you need to make sure that if youNothing, whatever else you're handing aMaybeto *also* gets aNothing.Notice that, unlike in [
map](#map) or its variants, the originalmaybeis not involved in constructing the newMaybe.```ts import Maybe from 'true-myth/maybe';
const justA = Maybe.just('A'); const justB = Maybe.just('B'); const nothing: Maybe = nothing();
console.log(Maybe.and(justB, justA).toString()); // Just(B) console.log(Maybe.and(justB, nothing).toString()); // Nothing console.log(Maybe.and(nothing, justA).toString()); // Nothing console.log(Maybe.and(nothing, nothing).toString()); // Nothing ```
T The type of the initial wrapped value. U The type of the wrapped value of the returned
Maybe.Parameter andMaybe
The
Maybeinstance to return ifmaybeisJustParameter maybe
The
Maybeinstance to check.Nothingif the originalmaybeisNothing, orandMaybeif the originalmaybeisJust.
function andThen
andThen: { <T extends {}, U extends {}>( thenFn: (t: T) => Maybe<U>, maybe: Maybe<T> ): Maybe<U>; <T extends {}, R extends AnyMaybe>(thenFn: (t: T) => R, maybe: Maybe<T>): Maybe< ValueFor<R> >; <T extends {}, U extends {}>(thenFn: (t: T) => Maybe<U>): ( maybe: Maybe<T> ) => Maybe<U>; <T extends {}, R extends AnyMaybe>(thenFn: (t: T) => R): ( maybe: Maybe<T> ) => Maybe<ValueFor<R>>;};Apply a function to the wrapped value if and return a new
Justcontaining the resulting value; or return ifNothing.This differs from in that
thenFnreturns anotherMaybe. You can useandThento combine two functions which *both* create aMaybefrom an unwrapped type.You may find the
.thenmethod on an ES6Promisehelpful for comparison: if you have aPromise, you can pass itsthenmethod a callback which returns anotherPromise, and the result will not be a *nested* promise, but a singlePromise. The difference is thatPromise#thenunwraps *all* layers to only ever return a singlePromisevalue, whereasMaybe.andThenwill not unwrap nestedMaybes.> [!NOTE] This is sometimes also known as
bind, but *not* aliased as such > because [bindalready means something in JavaScript][bind].[bind]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
#### Example
(This is a somewhat contrived example, but it serves to show the way the function behaves.)
```ts import Maybe, { andThen, toString } from 'true-myth/maybe';
// string -> Maybe const toMaybeLength = (s: string) => Maybe.of(s.length);
// Maybe const aMaybeString = Maybe.of('Hello, there!');
// Maybe const resultingLength = andThen(toMaybeLength, aMaybeString); console.log(toString(resultingLength)); // 13 ```
Note that the result is not
Just(Just(13)), butJust(13)!T The type of the wrapped value. U The type of the wrapped value in the resulting
Maybe.Parameter thenFn
The function to apply to the wrapped
TifmaybeisJust.Parameter maybe
The
Maybeto evaluate and possibly apply a function to the contents of.Returns
The result of the
thenFn(a newMaybe) ifmaybeis aJust, otherwiseNothingifmaybeis aNothing.
function ap
ap: { <T extends {}, U extends {}>( maybeFn: Maybe<(t: T) => U>, maybe: Maybe<T> ): Maybe<T | U>; <T extends {}, U extends {}>(maybeFn: Maybe<(t: T) => U>): ( maybe: Maybe<T> ) => Maybe<T | U>;};Allows you to *apply* (thus
ap) a value to a function without having to take either out of the context of their s. This does mean that the transforming function is itself within aMaybe, which can be hard to grok at first but lets you do some very elegant things. For example,apallows you to this:```ts import { just, nothing } from 'true-myth/maybe';
const one = just(1); const five = just(5); const none = nothing();
const add = (a: number) => (b: number) => a + b; const maybeAdd = just(add);
maybeAdd.ap(one).ap(five); // Just(6) maybeAdd.ap(one).ap(none); // Nothing maybeAdd.ap(none).ap(five) // Nothing ```
Without
ap, you'd need to do something like a nestedmatch:```ts import { just, nothing } from 'true-myth/maybe';
const one = just(1); const five = just(5); const none = nothing();
one.match({ Just: n => five.match({ Just: o => just(n + o), Nothing: () => nothing(), }), Nothing: () => nothing(), }); // Just(6)
one.match({ Just: n => none.match({ Just: o => just(n + o), Nothing: () => nothing(), }), Nothing: () => nothing(), }); // Nothing
none.match({ Just: n => five.match({ Just: o => just(n + o), Nothing: () => nothing(), }), Nothing: () => nothing(), }); // Nothing ```
And this kind of thing comes up quite often once you're using
Maybeto handle optionality throughout your application.For another example, imagine you need to compare the equality of two ImmutableJS data structures, where a
===comparison won't work. Withap, that's as simple as this:```ts import Maybe from 'true-myth/maybe'; import { is as immutableIs, Set } from 'immutable';
const is = (first: unknown) => (second: unknown) => immutableIs(first, second);
const x = Maybe.of(Set.of(1, 2, 3)); const y = Maybe.of(Set.of(2, 3, 4));
Maybe.of(is).ap(x).ap(y); // Just(false) ```
Without
ap, we're back to that gnarly nestedmatch:```ts import Maybe, { just, nothing } from 'true-myth/maybe'; import { is, Set } from 'immutable';
const x = Maybe.of(Set.of(1, 2, 3)); const y = Maybe.of(Set.of(2, 3, 4));
x.match({ Just: iX => y.match({ Just: iY => Maybe.just(is(iX, iY)), Nothing: () => Maybe.nothing(), }) Nothing: () => Maybe.nothing(), }); // Just(false) ```
In summary: anywhere you have two
Maybeinstances and need to perform an operation that uses both of them,apis your friend.Two things to note, both regarding *currying*:
1. All functions passed to
apmust be curried. That is, they must be of the form (for add)(a: number) => (b: number) => a + b, *not* the more usual(a: number, b: number) => a + byou see in JavaScript more generally.(Unfortunately, these do not currently work with lodash or Ramda's
curryhelper functions. A future update to the type definitions may make that work, but the intermediate types produced by those helpers and the more general function types expected by this function do not currently align.)2. You will need to call
apas many times as there are arguments to the function you're dealing with. So in the case of thisadd3function, which has the "arity" (function argument count) of 3 (aandb), you'll need to callaptwice: once fora, and once forb. To see why, let's look at what the result in each phase is:```ts const add3 = (a: number) => (b: number) => (c: number) => a + b + c;
const maybeAdd = just(add3); // Just((a: number) => (b: number) => (c: number) => a + b + c) const maybeAdd1 = maybeAdd.ap(just(1)); // Just((b: number) => (c: number) => 1 + b + c) const maybeAdd1And2 = maybeAdd1.ap(just(2)) // Just((c: number) => 1 + 2 + c) const final = maybeAdd1.ap(just(3)); // Just(4) ```
So for
toString, which just takes a single argument, you would only need to callaponce.```ts const toStr = (v: { toString(): string }) => v.toString(); just(toStr).ap(12); // Just("12") ```
One other scenario which doesn't come up *quite* as often but is conceivable is where you have something that may or may not actually construct a function for handling a specific
Maybescenario. In that case, you can wrap the possibly-present inapand then wrap the values to apply to the function to inMaybethemselves.__Aside:__
apis not namedapplybecause of the overlap with JavaScript's existing [apply] function – and although strictly speaking, there isn't any direct overlap (Maybe.applyandFunction.prototype.applydon't intersect at all) it's useful to have a different name to avoid implying that they're the same.[
apply]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/applyParameter maybeFn
maybe a function from T to U
Parameter maybe
maybe a T to apply to
fn
function equals
equals: { <T extends {}>(mb: Maybe<T>, ma: Maybe<T>): boolean; <T extends {}>(mb: Maybe<T>): (ma: Maybe<T>) => boolean;};Allows quick triple-equal equality check between the values inside two instances without having to unwrap them first.
```ts const a = Maybe.of(3); const b = Maybe.of(3); const c = Maybe.of(null); const d = Maybe.nothing();
Maybe.equals(a, b); // true Maybe.equals(a, c); // false Maybe.equals(c, d); // true ```
Parameter mb
A
maybeto compare to.Parameter ma
A
maybeinstance to check.
function find
find: { <T extends {}, U extends T>( predicate: NarrowingPredicate<T, U>, array: AnyArray<T> ): Maybe<U>; <T extends {}, U extends T>(predicate: NarrowingPredicate<T, U>): ( array: AnyArray<T> ) => Maybe<U>; <T extends {}>(predicate: Predicate<T>, array: AnyArray<T>): Maybe<T>; <T extends {}>(predicate: Predicate<T>): (array: AnyArray<T>) => Maybe<T>;};Safely search for an element in an array.
This function behaves like
Array.prototype.find, but returnsMaybe<T>instead ofT | undefined.## Examples
The basic form is:
```ts import Maybe from 'true-myth/maybe';
let array = [1, 2, 3]; Maybe.find(v => v > 1, array); // Just(2) Maybe.find(v => v < 1, array); // Nothing ```
The function is curried so you can use it in a functional chain. For example (leaving aside error handling on a bad response for simplicity), suppose the url
https://arrays.example.comreturned a JSON payload with the typeArray<{ count: number, name: string }>, and we wanted to get the first of these wherecountwas at least 100. We could write this:```ts import Maybe from 'true-myth/maybe';
type Item = { count: number; name: string }; type Response = Array;
// curried variant! const findAtLeast100 = Maybe.find(({ count }: Item) => count > 100);
fetch('https://arrays.example.com') .then(response => response.json() as Response) .then(findAtLeast100) .then(found => { if (found.isJust) { console.log(
The matching value is ${found.value.name}!); } }); ```Parameter predicate
A function to execute on each value in the array, returning
truewhen the item in the array matches the condition. The signature forpredicateis identical to the signature for the first argument toArray.prototype.find. The function is called once for each element of the array, in ascending order, until it finds one where predicate returns true. If such an element is found, find immediately returns that element value wrapped inJust. Otherwise,Maybe.findreturnsNothing.Parameter array
The array to search using the predicate.
function first
first: { <T extends {}>(array: AnyArray<T>): Maybe<Just<T>>; (array: AnyArray<null>): Maybe<Nothing<{}>>; <T extends {}>(array: AnyArray<T>): Maybe<Maybe<T>>;};Safely get the first item from a list, returning the first item if the array has at least one item in it, or if it is empty.
This produces a
Maybe<Maybe<T>>rather thanMaybe<T>to distinguish between arrays that includenullorundefinedand empty arrays.## Examples
```ts import { first } from 'true-myth/maybe';
let empty = []; first(empty); // => Nothing
let full = [1, 2, 3]; first(full); // => Just(Just(1))
let mixed = [undefined, 1]; first(mixed); // => Just(Nothing) ```
> [!NOTE] > Unfortunately, it is not possible to distinguish between these > statically in a single call signature in a reasonable way: we do not want to > change the types and runtime result produced by calling this function simply > because the input array had *its* type changed (to include, or not include, >
nullorundefined). Although the types and runtime could be guaranteed > to align, correctly doing so would require every item in the array to check > whether *any* items arenullorundefined, making the performance linear > on the size of the array ($O(n)$) instead of constant time ($O(1)$). This is > _quite_ undesirable!Parameter array
The array to get the first item from.
function flatten
flatten: <T extends {}>(nested: Maybe<Maybe<T>>) => Maybe<T>;Given a nested
Maybe, remove one layer of nesting.For example, given a
Maybe<Maybe<string>>, the resulting type after using this function will beMaybe<string>.## Note
This function only works when the value wrapped in
Maybeis anotherMaybe. If you have aMaybe<string>orMaybe<number>, this function won't work. If you have aMaybe<Maybe<string>>, then you can callmaybe.flatten(theMaybe)to get back aMaybe<string>.## Examples
```ts import * as maybe from 'true-myth/maybe';
const nested = maybe.just(maybe.just('hello')); const flattened = maybe.flatten(nested); // Maybe console.log(flattened); // Just('hello')
const nestedNothing = maybe.just(maybe.nothing()); const flattenedNothing = maybe.flatten(nestedNothing); // Maybe console.log(flattenedNothing); // Nothing
const nothingNested = maybe.nothing<Maybe>(); const flattenedOuter = maybe.flatten(nothingNested); // Maybe console.log(flattenedOuter); // Nothing ```
function fromJSON
fromJSON: <T extends {}>(json: MaybeJSON<T>) => Maybe<T>;Given a object, convert it into a .
Note that this is not designed for parsing data off the wire, but requires you to have *already* parsed it into the format.
Parameter json
The value to convert to JSON
Returns
The JSON representation of the
Maybe
function get
get: { <T extends { [key: string]: unknown }, K extends keyof T>( key: K, maybeObj: Maybe<T> ): Maybe<NonNullable<T[K]>>; <T extends { [key: string]: unknown }, K extends keyof T>(key: K): ( maybeObj: Maybe<T> ) => Maybe<NonNullable<T[K]>>;};Safely extract a key from a of an object, returning if the key has a value on the object and if it does not. (Like but operating on a
Maybe<T>rather than directly on aT.)The check is type-safe: you won't even be able to compile if you try to look up a property that TypeScript *knows* doesn't exist on the object.
```ts import { get, just, nothing } from 'true-myth/maybe';
type Person = { name?: string };
const me: Maybe = just({ name: 'Chris' }); console.log(get('name', me)); // Just('Chris')
const nobody = nothing(); console.log(get('name', nobody)); // Nothing ```
However, it also works correctly with dictionary types:
```ts import { get, just } from 'true-myth/maybe';
type Dict = { [key: string]: T };
const score: Maybe<Dict> = just({ player1: 0, player2: 1 });
console.log(get('player1', score)); // Just(0) console.log(get('player2', score)); // Just(1) console.log(get('player3', score)); // Nothing ```
The order of keys is so that it can be partially applied:
```ts import { get, just } from 'true-myth/maybe';
type Person = { name?: string };
const lookupName = get('name');
const me: Person = { name: 'Chris' }; console.log(lookupName(me)); // Just('Chris')
const nobody: Person = {}; console.log(lookupName(nobody)); // Nothing ```
Parameter key
The key to pull out of the object.
Parameter maybeObj
The object to look up the key from.
function inspect
inspect: { <T extends {}>(fn: (value: T) => void, maybe: Maybe<T>): Maybe<T>; <T extends {}>(fn: (value: T) => void): (maybe: Maybe<T>) => Maybe<T>;};Run a side effect with the wrapped value without modifying the .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Maybeis a , and the originalMaybeis returned unchanged for further operations.```ts import * as maybe from 'true-myth/maybe';
const log = (value: unknown) => console.log(value); const double = (value: number) => value * 2;
// Logs
42then84, and returnsJust(84). const aJust = maybe.just(42); maybe.inspect( maybe.map( double, maybe.inspect( log, aJust ) ) );// Does not log anything, and returns
Nothing. const aNothing = maybe.nothing(); maybe.inspect( maybe.map( double, maybe.inspect( log, aNothing ) ) ); ```T The type of the wrapped value
Parameter fn
The function to call with the wrapped value (only called for Just)
Parameter maybe
The Maybe to inspect
Returns
The original Maybe, unchanged
function isInstance
isInstance: { <T extends {}>(item: Maybe<T>): item is Maybe<T>; (item: unknown): item is Maybe<{}>;};Determine whether an item is an instance of .
Parameter item
The item to check.
function isJust
isJust: <T extends {}>(maybe: Maybe<T>) => maybe is Just<T>;Is the a ?
T The type of the item contained in the
Maybe.Parameter maybe
The
Maybeto check.Returns
A type guarded
Just.
function isNothing
isNothing: <T extends {}>(maybe: Maybe<T>) => maybe is Nothing<T>;Is the a ?
T The type of the item contained in the
Maybe.Parameter maybe
The
Maybeto check.Returns
A type guarded
Nothing.
function last
last: { <T extends {}>(array: AnyArray<T>): Maybe<Just<T>>; (array: AnyArray<null>): Maybe<Nothing<never>>; <T extends {}>(array: AnyArray<T>): Maybe<Maybe<T>>;};Safely get the last item from a list, returning the last item if the array has at least one item in it, or if it is empty.
This produces a
Maybe<Maybe<T>>rather thanMaybe<T>to distinguish between arrays that includenullorundefinedand empty arrays.## Examples
```ts import { last } from 'true-myth/maybe';
let empty = []; last(empty); // => Nothing
let full = [1, 2, 3]; last(full); // => Just(Just(3))
let mixed = [1, null, 2, null]; last(mixed); // Just(Nothing) ```
> [!NOTE] > Unfortunately, it is not possible to distinguish between these > statically in a single call signature in a reasonable way: we do not want to > change the types and runtime result produced by calling this function simply > because the input array had *its* type changed (to include, or not include, >
nullorundefined). Although the types and runtime could be guaranteed > to align, correctly doing so would require every item in the array to check > whether *any* items arenullorundefined, making the performance linear > on the size of the array ($O(n)$) instead of constant time ($O(1)$). This is > _quite_ undesirable!Parameter array
The array to get the first item from.
function map
map: { <T extends {}, U extends {}>(mapFn: (t: T) => U): (maybe: Maybe<T>) => Maybe<U>; <T extends {}, U extends {}>(mapFn: (t: T) => U, maybe: Maybe<T>): Maybe<U>;};Map over a instance: apply the function to the wrapped value if the instance is , and return if the instance is
Nothing.mapworks a lot likeArray.prototype.map:MaybeandArrayare both containers* for other things. If you have no items in an array of numbers namedfooand callfoo.map(x => x + 1), you'll still just have an array with nothing in it. But if you have any items in the array ([2, 3]), and you callfoo.map(x => x + 1)on it, you'll get a new array with each of those items inside the array "container" transformed ([3, 4]).That's exactly what's happening with
map. If the container is *empty* – theNothingvariant – you just get back an empty container. If the container has something in it – theJustvariant – you get back a container with the item inside transformed.(So... why not just use an array? The biggest reason is that an array can be any length. With a
Maybe, we're capturing the idea of "something or nothing" rather than "0 to n" items. And this lets us implement a whole set of *other* interfaces, like those in this module.)```ts const length = (s: string) => s.length;
const justAString = Maybe.just('string'); const justTheStringLength = map(length, justAString); console.log(justTheStringLength.toString()); // Just(6)
const notAString = Maybe.nothing(); const notAStringLength = map(length, notAString); console.log(notAStringLength.toString()); // "Nothing" ```
T The type of the wrapped value. U The type of the wrapped value of the returned
Maybe.Parameter mapFn
The function to apply the value to if
MaybeisJust.Returns
A function accepting a
Maybe<T>, which will produceMaybe<U>after applyingmapFn.Map over a instance: apply the function to the wrapped value if the instance is , and return if the instance is
Nothing.mapworks a lot likeArray.prototype.map:MaybeandArrayare both containers* for other things. If you have no items in an array of numbers namedfooand callfoo.map(x => x + 1), you'll still just have an array with nothing in it. But if you have any items in the array ([2, 3]), and you callfoo.map(x => x + 1)on it, you'll get a new array with each of those items inside the array "container" transformed ([3, 4]).That's exactly what's happening with
map. If the container is *empty* – theNothingvariant – you just get back an empty container. If the container has something in it – theJustvariant – you get back a container with the item inside transformed.(So... why not just use an array? The biggest reason is that an array can be any length. With a
Maybe, we're capturing the idea of "something or nothing" rather than "0 to n" items. And this lets us implement a whole set of *other* interfaces, like those in this module.)```ts const length = (s: string) => s.length;
const justAString = Maybe.just('string'); const justTheStringLength = map(length, justAString); console.log(justTheStringLength.toString()); // Just(6)
const notAString = Maybe.nothing(); const notAStringLength = map(length, notAString); console.log(notAStringLength.toString()); // "Nothing" ```
T The type of the wrapped value. U The type of the wrapped value of the returned
Maybe.Parameter mapFn
The function to apply the value to if
MaybeisJust.Parameter maybe
The
Maybeinstance to map over.Returns
A new
Maybewith the result of applyingmapFnto the value in aJust, orNothingifmaybeisNothing.
function mapOr
mapOr: { <T extends {}, U extends {}>(orU: U, mapFn: (t: T) => U, maybe: Maybe<T>): U; <T extends {}, U extends {}>(orU: U, mapFn: (t: T) => U): (maybe: Maybe<T>) => U; <T extends {}, U extends {}>(orU: U): ( mapFn: (t: T) => U ) => (maybe: Maybe<T>) => U;};Map over a instance and get out the value if
maybeis a , or return a default value ifmaybeis a .```ts const length = (s: string) => s.length;
const justAString = Maybe.just('string'); const theStringLength = mapOr(0, length, justAString); console.log(theStringLength); // 6
const notAString = Maybe.nothing(); const notAStringLength = mapOr(0, length, notAString) console.log(notAStringLength); // 0 ```
T The type of the wrapped value. U The type of the wrapped value of the returned
Maybe.Parameter orU
The default value to use if
maybeisNothingParameter mapFn
The function to apply the value to if
MaybeisJustParameter maybe
The
Maybeinstance to map over.
function mapOrElse
mapOrElse: { <T extends {}, U extends {}>( orElseFn: () => U, mapFn: (t: T) => U, maybe: Maybe<T> ): U; <T extends {}, U extends {}>(orElseFn: () => U, mapFn: (t: T) => U): ( maybe: Maybe<T> ) => U; <T extends {}, U extends {}>(orElseFn: () => U): ( mapFn: (t: T) => U ) => (maybe: Maybe<T>) => U;};Map over a instance and get out the value if
maybeis a , or use a function to construct a default value ifmaybeis .```ts const length = (s: string) => s.length; const getDefault = () => 0;
const justAString = Maybe.just('string'); const theStringLength = mapOrElse(getDefault, length, justAString); console.log(theStringLength); // 6
const notAString = Maybe.nothing(); const notAStringLength = mapOrElse(getDefault, length, notAString) console.log(notAStringLength); // 0 ```
T The type of the wrapped value. U The type of the wrapped value of the returned
Maybe.Parameter orElseFn
The function to apply if
maybeisNothing.Parameter mapFn
The function to apply to the wrapped value if
maybeisJustParameter maybe
The
Maybeinstance to map over.
function match
match: { <T extends {}, A>(matcher: Matcher<T, A>, maybe: Maybe<T>): A; <T extends {}, A>(matcher: Matcher<T, A>): (m: Maybe<T>) => A;};Performs the same basic functionality as , but instead of simply unwrapping the value if it is and applying a value to generate the same default type if it is , lets you supply functions which may transform the wrapped type if it is
Justor get a default value forNothing.This is kind of like a poor man's version of pattern matching, which JavaScript currently lacks.
Instead of code like this:
```ts import Maybe from 'true-myth/maybe';
const logValue = (mightBeANumber: Maybe) => { const valueToLog = Maybe.mightBeANumber.isJust ? mightBeANumber.value.toString() : 'Nothing to log.';
console.log(valueToLog); }; ```
...we can write code like this:
```ts import { match } from 'true-myth/maybe';
const logValue = (mightBeANumber: Maybe) => { const value = match( { Just: n => n.toString(), Nothing: () => 'Nothing to log.', }, mightBeANumber );
console.log(value); }; ```
This is slightly longer to write, but clearer: the more complex the resulting expression, the hairier it is to understand the ternary. Thus, this is especially convenient for times when there is a complex result, e.g. when rendering part of a React component inline in JSX/TSX.
Parameter matcher
A lightweight object defining what to do in the case of each variant.
Parameter maybe
The
maybeinstance to check.
function or
or: { <T extends {}>(defaultMaybe: Maybe<T>, maybe: Maybe<T>): Maybe<T>; <T extends {}>(defaultMaybe: Maybe<T>): (maybe: Maybe<T>) => Maybe<T>;};Provide a fallback for a given . Behaves like a logical
or: if themaybevalue is a , returns thatmaybe; otherwise, returns thedefaultMaybevalue.This is useful when you want to make sure that something which takes a
Maybealways ends up getting aJustvariant, by supplying a default value for the case that you currently have a nothing.```ts import Maybe from 'true-utils/maybe';
const justA = Maybe.just("a"); const justB = Maybe.just("b"); const aNothing: Maybe = nothing();
console.log(Maybe.or(justB, justA).toString()); // Just(A) console.log(Maybe.or(aNothing, justA).toString()); // Just(A) console.log(Maybe.or(justB, aNothing).toString()); // Just(B) console.log(Maybe.or(aNothing, aNothing).toString()); // Nothing ```
T The type of the wrapped value.
Parameter defaultMaybe
The
Maybeto use ifmaybeis aNothing.Parameter maybe
The
Maybeinstance to evaluate.Returns
maybeif it is aJust, otherwisedefaultMaybe.
function orElse
orElse: { <T extends {}, R extends AnyMaybe>(elseFn: () => R, maybe: Maybe<T>): Maybe< ValueFor<R> >; <T extends {}, R extends AnyMaybe>(elseFn: () => R): ( maybe: Maybe<T> ) => Maybe<ValueFor<R>>;};Like , but using a function to construct the alternative .
Sometimes you need to perform an operation using other data in the environment to construct the fallback value. In these situations, you can pass a function (which may be a closure) as the
elseFnto generate the fallbackMaybe<T>.Useful for transforming empty scenarios based on values in context.
T The type of the wrapped value.
Parameter elseFn
The function to apply if
maybeisNothingParameter maybe
The
maybeto use if it isJust.Returns
The
maybeif it isJust, or theMaybereturned byelseFnif themaybeisNothing.
function property
property: { <T, K extends keyof T>(key: K, obj: T): Maybe<NonNullable<T[K]>>; <T, K extends keyof T>(key: K): (obj: T) => Maybe<NonNullable<T[K]>>;};Safely extract a key from an object, returning if the key has a value on the object and if it does not.
The check is type-safe: you won't even be able to compile if you try to look up a property that TypeScript *knows* doesn't exist on the object.
```ts type Person = { name?: string };
const me: Person = { name: 'Chris' }; console.log(Maybe.property('name', me)); // Just('Chris')
const nobody: Person = {}; console.log(Maybe.property('name', nobody)); // Nothing ```
However, it also works correctly with dictionary types:
```ts import * as maybe from 'true-myth/maybe';
type Dict = { [key: string]: T };
const score: Dict = { player1: 0, player2: 1 };
console.log(maybe.property('player1', score)); // Just(0) console.log(maybe.property('player2', score)); // Just(1) console.log(maybe.property('player3', score)); // Nothing ```
The order of keys is so that it can be partially applied:
```ts type Person = { name?: string };
const lookupName = maybe.property('name');
const me: Person = { name: 'Chris' }; console.log(lookupName(me)); // Just('Chris')
const nobody: Person = {}; console.log(lookupName(nobody)); // Nothing ```
Parameter key
The key to pull out of the object.
Parameter obj
The object to look up the key from.
function safe
safe: < F extends AnyFunction, P extends Parameters<F>, R extends NonNullable<ReturnType<F>>>( fn: F) => (...params: P) => Maybe<R>;Transform a function from a normal JS function which may return
nullorundefinedto a function which returns a instead.For example, dealing with the
Document#querySelectorDOM API involves a lot* of things which can benull:```ts const foo = document.querySelector('#foo'); let width: number; if (foo !== null) { width = foo.getBoundingClientRect().width; } else { width = 0; }
const getStyle = (el: HTMLElement, rule: string) => el.style[rule]; const bar = document.querySelector('.bar'); let color: string; if (bar != null) { let possibleColor = getStyle(bar, 'color'); if (possibleColor !== null) { color = possibleColor; } else { color = 'black'; } } ```
(Imagine in this example that there were more than two options: the simplifying workarounds you commonly use to make this terser in JS, like the ternary operator or the short-circuiting
||or??operators, eventually become very confusing with more complicated flows.)We can work around this with
Maybe, always wrapping each layer in invocations, and this is *somewhat* better:```ts import Maybe from 'true-myth/maybe';
const aWidth = Maybe.of(document.querySelector('#foo')) .map(el => el.getBoundingClientRect().width) .unwrapOr(0);
const aColor = Maybe.of(document.querySelector('.bar')) .andThen(el => Maybe.of(getStyle(el, 'color')) .unwrapOr('black'); ```
With
safe, though, you can create a transformed version of a function once* and then be able to use it freely throughout your codebase, *always* getting back aMaybe:```ts import { safe } from 'true-myth/maybe';
const querySelector = safe(document.querySelector.bind(document)); const safelyGetStyle = safe(getStyle);
const aWidth = querySelector('#foo') .map(el => el.getBoundingClientRect().width) .unwrapOr(0);
const aColor = querySelector('.bar') .andThen(el => safelyGetStyle(el, 'color')) .unwrapOr('black'); ```
Parameter fn
The function to transform; the resulting function will have the exact same signature except for its return type.
function toJSON
toJSON: <T extends {}>(maybe: Maybe<T>) => Serialized<T>;Create an
Objectrepresentation of a instance.Useful for serialization.
JSON.stringify()uses it.Parameter maybe
The value to convert to JSON
Returns
The JSON representation of the
Maybe
function toString
toString: <T extends {}>(maybe: Maybe<T>) => string;Create a
Stringrepresentation of a instance.A instance will be
Just(<representation of the value>), where the representation of the value is simply the value's owntoStringrepresentation. For example:| call | output | |----------------------------------------|-------------------------| |
toString(Maybe.of(42))|Just(42)| |toString(Maybe.of([1, 2, 3]))|Just(1,2,3)| |toString(Maybe.of({ an: 'object' }))|Just([object Object])| |toString(Maybe.nothing())|Nothing|T The type of the wrapped value; its own
.toStringwill be used to print the interior contents of theJustvariant.Parameter maybe
The value to convert to a string.
Returns
The string representation of the
Maybe.
function transposeArray
transposeArray: () => any;Given an array or tuple of s, return a
Maybeof the array or tuple values.- Given an array of type
Array<Maybe<A> | Maybe<B>>, the resulting type isMaybe<Array<A | B>>. - Given a tuple of type[Maybe<A>, Maybe<B>], the resulting type isMaybe<[A, B]>.If any of the items in the array or tuple are , the whole result is
Nothing. If all items in the array or tuple are , the whole result isJust.## Examples
Given an array with a mix of
Maybetypes in it, bothallJustandmixedhere will have the typeMaybe<Array<string | number>>, but will beJustandNothingrespectively.```ts import Maybe, { transposeArray } from 'true-myth/maybe';
let valid = [Maybe.just(2), Maybe.just('three')]; let allJust = transposeArray(valid); // => Just([2, 'three']);
let invalid = [Maybe.just(2), Maybe.nothing()]; let mixed = transposeArray(invalid); // => Nothing ```
When working with a tuple type, the structure of the tuple is preserved. Here, for example,
resulthas the typeMaybe<[string, number]>and will beNothing:```ts import Maybe, { transposeArray } from 'true-myth/maybe';
type Tuple = [Maybe, Maybe];
let invalid: Tuple = [Maybe.just('wat'), Maybe.nothing()]; let result = transposeArray(invalid); // => Nothing ```
If all of the items in the tuple are
Just, the result isJustwrapping the tuple of the values of the items. Here, for example,resultagain has the typeMaybe<[string, number]>and will beJust(['hey', 12]:```ts import Maybe, { transposeArray } from 'true-myth/maybe';
type Tuple = [Maybe, Maybe];
let valid: Tuple = [Maybe.just('hey'), Maybe.just(12)]; let result = transposeArray(valid); // => Just(['hey', 12]) ```
Parameter maybes
The
Maybes to resolve to a singleMaybe.
function unwrapOr
unwrapOr: { <T extends {}, U>(defaultValue: U, maybe: Maybe<T>): T | U; <T extends {}, U>(defaultValue: U): (maybe: Maybe<T>) => T | U;};Safely get the value out of a .
Returns the content of a or
defaultValueif . This is the recommended way to get a value out of aMaybemost of the time.```ts import Maybe from 'true-myth/maybe';
const notAString = Maybe.nothing(); const isAString = Maybe.just('look ma! some characters!');
console.log(Maybe.unwrapOr('', notAString)); // "" console.log(Maybe.unwrapOr('', isAString)); // "look ma! some characters!" ```
T The type of the wrapped value.
Parameter defaultValue
The value to return if
maybeis aNothing.Parameter maybe
The
Maybeinstance to unwrap if it is aJust.Returns
The content of
maybeif it is aJust, otherwisedefaultValue.
function unwrapOrElse
unwrapOrElse: { <T extends {}, U>(orElseFn: () => U, maybe: Maybe<T>): T | U; <T extends {}, U>(orElseFn: () => U): (maybe: Maybe<T>) => T | U;};Safely get the value out of a by returning the wrapped value if it is , or by applying
orElseFnif it is .This is useful when you need to *generate* a value (e.g. by using current values in the environment – whether preloaded or by local closure) instead of having a single default value available (as in ).
```ts import Maybe from 'true-myth/maybe';
// You can imagine that someOtherValue might be dynamic. const someOtherValue = 99; const handleNothing = () => someOtherValue;
const aJust = Maybe.just(42); console.log(Maybe.unwrapOrElse(handleNothing, aJust)); // 42
const aNothing = nothing(); console.log(Maybe.unwrapOrElse(handleNothing, aNothing)); // 99 ```
T The wrapped value.
Parameter orElseFn
A function used to generate a valid value if
maybeis aNothing.Parameter maybe
The
Maybeinstance to unwrap if it is aJustReturns
Either the content of
maybeor the value returned fromorElseFn.
interface Just
interface Just<T extends {}> extends MaybeImpl<T> {}A
Justinstance is the *present* variant instance of the type, representing the presence of a value which may be absent. For a full discussion, see the module docs.T The type wrapped in this
Justvariant ofMaybe.
interface MaybeConstructor
interface MaybeConstructor {}The public interface for the class *as a value*: a constructor and the associated static properties.
property just
just: typeof MaybeImpl.just;property nothing
nothing: typeof MaybeImpl.nothing;property of
of: typeof MaybeImpl.of;construct signature
new <T extends {}>(value?: T | null | undefined): Maybe<T>;interface Nothing
interface Nothing<T extends {}> extends Omit<MaybeImpl<T>, 'value'> {}A
Nothinginstance is the *absent* variant instance of the type, representing the presence of a value which may be absent. For a full discussion, see the module docs.T The type which would be wrapped in a variant of the .
interface NothingJSON
interface NothingJSON<_T = {}> {}property variant
variant: 'Nothing';type AnyArray
type AnyArray<T> = Array<T> | ReadonlyArray<T>;An array or a readonly array.
type AnyMaybe
type AnyMaybe = Maybe<{}>;A convenient way to name
Maybe<{}>.
type Matcher
type Matcher<T, A> = { Just: (value: T) => A; Nothing: () => A;};A lightweight object defining how to handle each variant of a .
type Maybe
type Maybe<T extends {}> = Just<T> | Nothing<T>;Mayberepresents a value which may () or may not () be present.
type MaybeJSON
type MaybeJSON<T extends {}> = JustJSON<T> | NothingJSON<T>;type NarrowingPredicate
type NarrowingPredicate<T, U extends T> = ( element: T, index: number, array: AnyArray<T>) => element is U;type Predicate
type Predicate<T> = (element: T, index: number, array: AnyArray<T>) => boolean;type Serialized
type Serialized<M extends {}> = M extends Maybe<infer U> ? MaybeJSON<Serialized<U>> : MaybeJSON<M>;type TransposedArray
type TransposedArray<T extends ReadonlyArray<Maybe<{}>>> = Array<unknown> extends T ? Maybe<{ -readonly [K in keyof T]: Unwrapped<T[K]>; }> : Maybe<{ [K in keyof T]: Unwrapped<T[K]>; }>;type Unwrapped
type Unwrapped<T> = T extends Maybe<infer U> ? Unwrapped<U> : T;type Variant
type Variant = keyof typeof Variant;namespace result
module 'dist/result.d.ts' {}A is a type representing the value result of a synchronous operation which may fail, with a successful value of type
Tor an error of typeE.If the result is a success, it is . If the result is a failure, it is .
For a deep dive on the type, see [the guide](/guide/understanding/result.md).
variable []
const []: any;variable []
const []: any;variable []
const []: any;variable A
const A: any;variable A
const A: any;variable A
const A: any;variable AnyResult
const AnyResult: any;variable AnyResult
const AnyResult: any;variable AnyResult
const AnyResult: any;variable err
const err: { <T = never, E = unknown>(): Result<T, Unit>; <T = never, E = unknown>(error: E): Result<T, E>;};Create an instance of .
If you need to create an instance with a specific type (as you do whenever you are not constructing immediately for a function return or as an argument to a function), you can use a type parameter:
```ts const notString = Result.err<number, string>('something went wrong'); ```
Note: passing nothing, or passing
nullorundefinedexplicitly, will produce aResult<T, Unit>, rather than producing the nonsensical and in practice quite annoyingResult<null, string>etc. See for more.```ts const normalResult = Result.err<number, string>('oh no'); const explicitUnit = Result.err<number, Unit>(Unit); const implicitUnit = Result.err<number, Unit>(); ```
In the context of an immediate function return, or an arrow function with a single expression value, you do not have to specify the types, so this can be quite convenient.
```ts type SomeData = { //... };
const isValid = (data: SomeData): boolean => { // true or false... }
const arrowValidate = (data: SomeData): Result<number, Unit> => isValid(data) ? Result.ok(42) : Result.err();
function fnValidate(data: someData): Result<number, Unit> { return isValid(data) ? Result.ok(42) : Result.err(); } ```
T The type of the item contained in the
Result.Parameter E
The error value to wrap in a
Result.Err.
variable IsResult
const IsResult: Symbol;variable ok
const ok: { (): Result<Unit, never>; <T, E = never>(value: T): Result<T, E> };Create an instance of .
If you need to create an instance with a specific type (as you do whenever you are not constructing immediately for a function return or as an argument to a function), you can use a type parameter:
```ts const yayNumber = Result.ok<number, string>(12); ```
Note: passing nothing, or passing
nullorundefinedexplicitly, will produce aResult<Unit, E>, rather than producing the nonsensical and in practice quite annoyingResult<null, string>etc. See for more.```ts const normalResult = Result.ok<number, string>(42); const explicitUnit = Result.ok<Unit, string>(Unit); const implicitUnit = Result.ok<Unit, string>(); ```
In the context of an immediate function return, or an arrow function with a single expression value, you do not have to specify the types, so this can be quite convenient.
```ts type SomeData = { //... };
const isValid = (data: SomeData): boolean => { // true or false... }
const arrowValidate = (data: SomeData): Result<Unit, string> => isValid(data) ? Result.ok() : Result.err('something was wrong!');
function fnValidate(data: someData): Result<Unit, string> { return isValid(data) ? Result.ok() : Result.err('something was wrong'); } ```
T The type of the item contained in the
Result.Parameter value
The value to wrap in a
Result.Ok.
variable readonly
const readonly: any;variable readonly
const readonly: any;variable Result
const Result: ResultConstructor;A
Resultrepresents success () or failure ().The behavior of this type is checked by TypeScript at compile time, and bears no runtime overhead other than the very small cost of the container object.
variable Variant
const Variant: { readonly Ok: 'Ok'; readonly Err: 'Err' };Discriminant for and variants of the type.
You can use the discriminant via the
variantproperty ofResultinstances if you need to match explicitly on it.
function all
all: (results: readonly []) => Result<[], never>;Given an array of results, return a new result if all results are or a new result if some result is .
## Examples
If all results are , return a new result containing an array of all given values:
```ts import Result, { all } from 'true-myth/result';
let result = all([ Result.ok(10), Result.ok(100), Result.ok(1000) ]);
console.log(result.toString()); // Ok(10,100,1000) ```
If any result is , return a new result containing the first encountered:
```ts import Result, { all } from 'true-myth/result';
let result = all([ Result.ok(10), Result.ok("something went wrong"), Result.err("something else went wrong") ]);
console.log(result.toString()); // Err(something went wrong) ```
Parameter results
The list of results. A new containing an array of all values if all results are , or a new result containing the first encountered.
A The type of the array or tuple of results.
function and
and: { <T, U, E>(andResult: Result<U, E>, result: Result<T, E>): Result<U, E>; <T, U, E>(andResult: Result<U, E>): (result: Result<T, E>) => Result<U, E>;};You can think of this like a short-circuiting logical "and" operation on a type. If
resultis , then the result is theandResult. Ifresultis , the result is theErr.This is useful when you have another
Resultvalue you want to provide if and only if* you have anOk– that is, when you need to make sure that if youErr, whatever else you're handing aResultto *also* gets thatErr.Notice that, unlike in [
map](#map) or its variants, the originalresultis not involved in constructing the newResult.```ts import { and, ok, err, toString } from 'true-myth/result';
const okA = ok('A'); const okB = ok('B'); const anErr = err({ so: 'bad' });
console.log(toString(and(okB, okA))); // Ok(B) console.log(toString(and(okB, anErr))); // Err([object Object]) console.log(toString(and(anErr, okA))); // Err([object Object]) console.log(toString(and(anErr, anErr))); // Err([object Object]) ```
T The type of the value wrapped in the
Okof theResult. U The type of the value wrapped in theOkof theandResult, i.e. the success type of theResultpresent if the checkedResultisOk. E The type of the value wrapped in theErrof theResult.Parameter andResult
The
Resultinstance to return ifresultisErr.Parameter result
The
Resultinstance to check.
function andThen
andThen: { <T, E, R extends AnyResult>(thenFn: (t: T) => R, result: Result<T, E>): Result< OkFor<R>, E | ErrFor<R> >; <T, E, R extends AnyResult>(thenFn: (t: T) => R): ( result: Result<T, E> ) => Result<OkFor<R>, E | ErrFor<R>>;};Apply a function to the wrapped value if and return a new
Okcontaining the resulting value; or if it is return it unmodified.This differs from
mapin thatthenFnreturns another . You can useandThento combine two functions which *both* create aResultfrom an unwrapped type.You may find the
.thenmethod on an ES6Promisehelpful for comparison: if you have aPromise, you can pass itsthenmethod a callback which returns anotherPromise, and the result will not be a *nested* promise, but a singlePromise. The difference is thatPromise#thenunwraps *all* layers to only ever return a singlePromisevalue, whereasResult.andThenwill not unwrap nestedResults.> [!NOTE] This is is sometimes also known as
bind, but *not* aliased as such > because [bindalready means something in JavaScript][bind].[bind]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
```ts import { ok, err, andThen, toString } from 'true-myth/result';
const toLengthAsResult = (s: string) => ok(s.length);
const anOk = ok('just a string'); const lengthAsResult = andThen(toLengthAsResult, anOk); console.log(toString(lengthAsResult)); // Ok(13)
const anErr = err(['srsly', 'whatever']); const notLengthAsResult = andThen(toLengthAsResult, anErr); console.log(toString(notLengthAsResult)); // Err(srsly,whatever) ```
T The type of the value wrapped in the
Okof theResult. U The type of the value wrapped in theOkof theResultreturned by thethenFn. E The type of the value wrapped in theErrof theResult.Parameter thenFn
The function to apply to the wrapped
TifmaybeisJust.Parameter result
The
Maybeto evaluate and possibly apply a function to.
function ap
ap: { <A, B, E>(resultFn: Result<(a: A) => B, E>, result: Result<A, E>): Result<B, E>; <A, B, E>(resultFn: Result<(a: A) => B, E>): ( result: Result<A, E> ) => Result<B, E>;};Allows you to *apply* (thus
ap) a value to a function without having to take either out of the context of their s. This does mean that the transforming function is itself within aResult, which can be hard to grok at first but lets you do some very elegant things. For example,apallows you to do this (using the method form, since nestingapcalls is awkward):```ts import { ap, ok, err } from 'true-myth/result';
const one = ok<number, string>(1); const five = ok<number, string>(5); const whoops = err<number, string>('oh no');
const add = (a: number) => (b: number) => a + b; const resultAdd = ok<typeof add, string>(add);
resultAdd.ap(one).ap(five); // Ok(6) resultAdd.ap(one).ap(whoops); // Err('oh no') resultAdd.ap(whoops).ap(five) // Err('oh no') ```
Without
ap, you'd need to do something like a nestedmatch:```ts import { ok, err } from 'true-myth/result';
const one = ok<number, string>(1); const five = ok<number, string>(5); const whoops = err<number, string>('oh no');
one.match({ Ok: n => five.match({ Ok: o => ok<number, string>(n + o), Err: e => err<number, string>(e), }), Err: e => err<number, string>(e), }); // Ok(6)
one.match({ Ok: n => whoops.match({ Ok: o => ok<number, string>(n + o), Err: e => err<number, string>(e), }), Err: e => err<number, string>(e), }); // Err('oh no')
whoops.match({ Ok: n => five.match({ Ok: o => ok(n + o), Err: e => err(e), }), Err: e => err(e), }); // Err('oh no') ```
And this kind of thing comes up quite often once you're using
Resultto handle errors throughout your application.For another example, imagine you need to compare the equality of two ImmutableJS data structures, where a
===comparison won't work. Withap, that's as simple as this:```ts import { ok } from 'true-myth/result'; import { is as immutableIs, Set } from 'immutable';
const is = (first: unknown) => (second: unknown) => immutableIs(first, second);
const x = ok(Set.of(1, 2, 3)); const y = ok(Set.of(2, 3, 4));
ok(is).ap(x).ap(y); // Ok(false) ```
Without
ap, we're back to that gnarly nestedmatch:```ts import Result, { ok, err } from 'true-myth/result'; import { is, Set } from 'immutable';
const x = ok(Set.of(1, 2, 3)); const y = ok(Set.of(2, 3, 4));
x.match({ Ok: iX => y.match({ Ok: iY => Result.of(is(iX, iY)), Err: (e) => ok(false), }) Err: (e) => ok(false), }); // Ok(false) ```
In summary: anywhere you have two
Resultinstances and need to perform an operation that uses both of them,apis your friend.Two things to note, both regarding *currying*:
1. All functions passed to
apmust be curried. That is, they must be of the form (for add)(a: number) => (b: number) => a + b, *not* the more usual(a: number, b: number) => a + byou see in JavaScript more generally.(Unfortunately, these do not currently work with lodash or Ramda's
curryhelper functions. A future update to the type definitions may make that work, but the intermediate types produced by those helpers and the more general function types expected by this function do not currently align.)2. You will need to call
apas many times as there are arguments to the function you're dealing with. So in the case of thisadd3function, which has the "arity" (function argument count) of 3 (aandb), you'll need to callaptwice: once fora, and once forb. To see why, let's look at what the result in each phase is:```ts const add3 = (a: number) => (b: number) => (c: number) => a + b + c;
const resultAdd = ok(add); // Ok((a: number) => (b: number) => (c: number) => a + b + c) const resultAdd1 = resultAdd.ap(ok(1)); // Ok((b: number) => (c: number) => 1 + b + c) const resultAdd1And2 = resultAdd1.ap(ok(2)) // Ok((c: number) => 1 + 2 + c) const final = maybeAdd1.ap(ok(3)); // Ok(4) ```
So for
toString, which just takes a single argument, you would only need to callaponce.```ts const toStr = (v: { toString(): string }) => v.toString(); ok(toStr).ap(12); // Ok("12") ```
One other scenario which doesn't come up *quite* as often but is conceivable is where you have something that may or may not actually construct a function for handling a specific
Resultscenario. In that case, you can wrap the possibly-present inapand then wrap the values to apply to the function to inResultthemselves.Because
Resultoften requires you to type out the full type parameterization on a regular basis, it's convenient to use TypeScript'stypeofoperator to write out the type of a curried function. For example, if you had a function that simply merged three strings, you might write it like this:```ts import Result from 'true-myth/result'; import { curry } from 'lodash';
const merge3Strs = (a: string, b: string, c: string) => string; const curriedMerge = curry(merge3Strs);
const fn = Result.ok<typeof curriedMerge, string>(curriedMerge); ```
The alternative is writing out the full signature long-form:
```ts const fn = Result.ok<(a: string) => (b: string) => (c: string) => string, string>(curriedMerge); ```
*Aside:**
apis not namedapplybecause of the overlap with JavaScript's existing [apply] function – and although strictly speaking, there isn't any direct overlap (Result.applyandFunction.prototype.applydon't intersect at all) it's useful to have a different name to avoid implying that they're the same.[
apply]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/applyParameter resultFn
result of a function from T to U
Parameter result
result of a T to apply to
fn
function equals
equals: { <T, E>(resultB: Result<T, E>, resultA: Result<T, E>): boolean; <T, E>(resultB: Result<T, E>): (resultA: Result<T, E>) => boolean;};Allows quick triple-equal equality check between the values inside two s without having to unwrap them first.
```ts const a = Result.of(3) const b = Result.of(3) const c = Result.of(null) const d = Result.nothing()
Result.equals(a, b) // true Result.equals(a, c) // false Result.equals(c, d) // true ```
Parameter resultB
A
maybeto compare to.Parameter resultA
A
maybeinstance to check.
function flatten
flatten: <T, E1, E2>(nested: Result<Result<T, E2>, E1>) => Result<T, E1 | E2>;Given a nested
Result, remove one layer of nesting.For example, given a
Result<Result<string, E2>, E1>, the resulting type after using this function will beResult<string, E1 | E2>.## Note
This function only works when the value wrapped in
Resultis anotherResult. If you have aResult<string, E>orResult<number, E>, this function won't work. If you have aResult<Result<string, E2>, E1>, then you can callresult.flatten(theResult)to get back aResult<string, E1 | E2>.## Examples
```ts import * as result from 'true-myth/result';
const nested = result.ok(result.ok('hello')); const flattened = result.flatten(nested); // Result<string, never> console.log(flattened); // Ok('hello')
const nestedError = result.ok(result.err('inner error')); const flattenedError = result.flatten(nestedError); // Result<never, string> console.log(flattenedError); // Err('inner error')
const errorNested = result.err<Result<string, string>, string>('outer error'); const flattenedOuter = result.flatten(errorNested); // Result<string, string> console.log(flattenedOuter); // Err('outer error') ```
function fromJSON
fromJSON: <T, E>(json: ResultJSON<T, E>) => Result<T, E>;Given a object, convert it into a .
Note that this is not designed for parsing data off the wire, but requires you to have *already* parsed it into the format.
Parameter json
The value to convert from a JSON object.
Returns
{Result<T,E>} The converted
Resulttype.
function inspect
inspect: { <T, E>(fn: (value: T) => void, result: Result<T, E>): Result<T, E>; <T, E>(fn: (value: T) => void): (result: Result<T, E>) => Result<T, E>;};Run a side effect with the wrapped value without modifying the .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Resultis , and the originalResultis returned unchanged for further chaining.```ts import * as result from 'true-myth/result';
const double = (n: number) => n * 2; const log = (value: unknown) => console.log(value);
// Logs
42then84, and returnsOk(84). const anOk = result.ok<number, string>(42); result.inspect( log, result.map( double, result.inspect( log, anOk ) ) );// Does not log anything, and returns
Err('error'). const anErr = result.err<number, string>('error'); result.inspect( log, result.map( double, result.inspect( log, anErr ) ) ); ```T The type of the wrapped value E The type of the error value
Parameter fn
The function to call with the wrapped value (only called for Ok)
Parameter result
The Result to inspect
Returns
The original Result, unchanged
function inspectErr
inspectErr: { <T, E>(fn: (error: E) => void, result: Result<T, E>): Result<T, E>; <T, E>(fn: (error: E) => void): (result: Result<T, E>) => Result<T, E>;};Run a side effect with the error value without modifying the .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Resultis , and the originalResultis returned unchanged for further chaining.> [!NOTE] > TypeScript type inference is limited with the curried form. You will need to > provide explicit type parameters. Alternatively, use the non-curried form > to get consistent type inference.
```ts import * as result from 'true-myth/result';
const double = (n: number) => n * 2; const logError = (value: unknown) => console.log('Got error:', value);
// Logs: "Got error: error" const anErr = result.err<number, string>('error'); result.inspectErr( logError, result.map( double, result.inspectErr( logError, anOk ) ) );
// Does not log anything, and returns
Ok(42). const anOk = result.ok<number, string>(42); result.inspectErr( logError, result.map( double, result.inspectErr( logError, anErr ) ) ); ```T The type of the wrapped value E The type of the error value
Parameter fn
The function to call with the error value (only called for Err)
Parameter result
The Result to inspect
Returns
The original Result, unchanged
Run a side effect with the error value without modifying the .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Resultis , and the originalResultis returned unchanged for further chaining.```ts import * as result from 'true-myth/result';
const double = (n: number) => n * 2; const logError = (value: unknown) => console.log('Got error:', value);
// Logs: "Got error: error" const anErr = result.err<number, string>('error'); result.inspectErr( logError, result.map( double, result.inspectErr( logError, anOk ) ) );
// Does not log anything, and returns
Ok(42). const anOk = result.ok<number, string>(42); result.inspectErr( logError, result.map( double, result.inspectErr( logError, anErr ) ) ); ```T The type of the wrapped value E The type of the error value
Parameter fn
The function to call with the error value (only called for Err)
Parameter result
The Result to inspect
Returns
The original Result, unchanged
function isErr
isErr: <T, E>(result: Result<T, E>) => result is Err<T, E>;Is the an ?
T The type of the item contained in the
Result.Parameter result
The
Resultto check.Returns
A type guarded
Err.
function isInstance
isInstance: { <T, E>(item: Result<T, E>): item is Result<T, E>; (item: unknown): item is Result<unknown, unknown>;};Determine whether an item is an instance of .
Parameter item
The item to check.
function isOk
isOk: <T, E>(result: Result<T, E>) => result is Ok<T, E>;Is the an ?
T The type of the item contained in the
Result.Parameter result
The
Resultto check.Returns
A type guarded
Ok.
function map
map: { <T, U, E>(mapFn: (t: T) => U, result: Result<T, E>): Result<U, E>; <T, U, E>(mapFn: (t: T) => U): (result: Result<T, E>) => Result<U, E>;};Map over a instance: apply the function to the wrapped value if the instance is , and return the wrapped error value wrapped as a new of the correct type (
Result<U, E>) if the instance isErr.mapworks a lot likeArray.prototype.map, but with one important difference. BothResultandArrayare containers for other kinds of items, but whereArray.prototype.maphas 0 to _n_ items, aResultalways has exactly one item, which is *either* a success or an error instance.Where
Array.prototype.mapwill apply the mapping function to every item in the array (if there are any),Result.mapwill only apply the mapping function to the (single) element if anOkinstance, if there is one.If you have no items in an array of numbers named
fooand call `foo.map(x => x + 1)`, you'll still some have an array with nothing in it. But if you have any items in the array ([2, 3]), and you callfoo.map(x => x + 1)on it, you'll get a new array with each of those items inside the array "container" transformed ([3, 4]).With this
map, theErrvariant is treated *by themapfunction* kind of the same way as the empty array case: it's just ignored, and you get back a newResultthat is still just the sameErrinstance. But if you have anOkvariant, the map function is applied to it, and you get back a newResultwith the value transformed, and still wrapped in anOk.```ts import { ok, err, map, toString } from 'true-myth/result'; const double = n => n * 2;
const anOk = ok(12); const mappedOk = map(double, anOk); console.log(toString(mappedOk)); // Ok(24)
const anErr = err("nothing here!"); const mappedErr = map(double, anErr); console.log(toString(mappedErr)); // Err(nothing here!) ```
T The type of the value wrapped in an
Okinstance, and taken as the argument to themapFn. U The type of the value wrapped in the newOkinstance after applyingmapFn, that is, the type returned bymapFn. E The type of the value wrapped in anErrinstance.Parameter mapFn
The function to apply the value to if
resultisOk.Parameter result
The
Resultinstance to map over.Returns
A new
Resultwith the result of applyingmapFnto the value in anOk, or else the originalErrvalue wrapped in the new instance.
function mapErr
mapErr: { <T, E, F>(mapErrFn: (e: E) => F, result: Result<T, E>): Result<T, F>; <T, E, F>(mapErrFn: (e: E) => F): (result: Result<T, E>) => Result<T, F>;};Map over a , exactly as in , but operating on the value wrapped in an instead of the value wrapped in the . This is handy for when you need to line up a bunch of different types of errors, or if you need an error of one shape to be in a different shape to use somewhere else in your codebase.
```ts import { ok, err, mapErr, toString } from 'true-myth/result';
const reason = (err: { code: number, reason: string }) => err.reason;
const anOk = ok(12); const mappedOk = mapErr(reason, anOk); console.log(toString(mappedOk)); // Ok(12)
const anErr = err({ code: 101, reason: 'bad file' }); const mappedErr = mapErr(reason, anErr); console.log(toString(mappedErr)); // Err(bad file) ```
T The type of the value wrapped in the
Okof theResult. E The type of the value wrapped in theErrof theResult. F The type of the value wrapped in theErrof a newResult, returned by themapErrFn.Parameter mapErrFn
The function to apply to the value wrapped in
Errifresultis anErr.Parameter result
The
Resultinstance to map over an error case for.
function mapOr
mapOr: { <T, U, E>(orU: U, mapFn: (t: T) => U, result: Result<T, E>): U; <T, U, E>(orU: U, mapFn: (t: T) => U): (result: Result<T, E>) => U; <T, U, E>(orU: U): (mapFn: (t: T) => U) => (result: Result<T, E>) => U;};Map over a instance as in [
map](#map) and get out the value ifresultis an , or return a default value ifresultis an .```ts import { ok, err, mapOr } from 'true-myth/result';
const length = (s: string) => s.length;
const anOkString = ok('a string'); const theStringLength = mapOr(0, length, anOkString); console.log(theStringLength); // 8
const anErr = err('uh oh'); const anErrMapped = mapOr(0, length, anErr); console.log(anErrMapped); // 0 ```
Parameter orU
The default value to use if
resultis anErr.Parameter mapFn
The function to apply the value to if
resultis anOk.Parameter result
The
Resultinstance to map over.
function mapOrElse
mapOrElse: { <T, U, E>(orElseFn: (err: E) => U, mapFn: (t: T) => U, result: Result<T, E>): U; <T, U, E>(orElseFn: (err: E) => U, mapFn: (t: T) => U): ( result: Result<T, E> ) => U; <T, U, E>(orElseFn: (err: E) => U): ( mapFn: (t: T) => U ) => (result: Result<T, E>) => U;};Map over a instance as in and get out the value if
resultis , or apply a function (orElseFn) to the value wrapped in the to get a default value.Like but using a function to transform the error into a usable value instead of simply using a default value.
```ts import { ok, err, mapOrElse } from 'true-myth/result';
const summarize = (s: string) =>
The response was: '${s}'; const getReason = (err: { code: number, reason: string }) => err.reason;const okResponse = ok("Things are grand here."); const mappedOkAndUnwrapped = mapOrElse(getReason, summarize, okResponse); console.log(mappedOkAndUnwrapped); // The response was: 'Things are grand here.'
const errResponse = err({ code: 500, reason: 'Nothing at this endpoint!' }); const mappedErrAndUnwrapped = mapOrElse(getReason, summarize, errResponse); console.log(mappedErrAndUnwrapped); // Nothing at this endpoint! ```
T The type of the wrapped
Okvalue. U The type of the resulting value from applyingmapFnto theOkvalue ororElseFnto theErrvalue. E The type of the wrappedErrvalue.Parameter orElseFn
The function to apply to the wrapped
Errvalue to get a usable value ifresultis anErr.Parameter mapFn
The function to apply to the wrapped
Okvalue ifresultis anOk.Parameter result
The
Resultinstance to map over.
function match
match: { <T, E, A>(matcher: Matcher<T, E, A>, result: Result<T, E>): A; <T, E, A>(matcher: Matcher<T, E, A>): (result: Result<T, E>) => A;};Performs the same basic functionality as , but instead of simply unwrapping the value if it is and applying a value to generate the same default type if it is , lets you supply functions which may transform the wrapped type if it is
Okor get a default value forErr.This is kind of like a poor man's version of pattern matching, which JavaScript currently lacks.
Instead of code like this:
```ts import Result, { isOk, match } from 'true-myth/result';
const logValue = (mightBeANumber: Result<number, string>) => { console.log( mightBeANumber.isOk ? mightBeANumber.value.toString() :
There was an error: ${unsafelyGetErr(mightBeANumber)}); }; ```...we can write code like this:
```ts import Result, { match } from 'true-myth/result';
const logValue = (mightBeANumber: Result<number, string>) => { const value = match( { Ok: n => n.toString(), Err: e =>
There was an error: ${e}, }, mightBeANumber ); console.log(value); }; ```This is slightly longer to write, but clearer: the more complex the resulting expression, the hairer it is to understand the ternary. Thus, this is especially convenient for times when there is a complex result, e.g. when rendering part of a React component inline in JSX/TSX.
Parameter matcher
A lightweight object defining what to do in the case of each variant.
Parameter result
The
resultinstance to check.Performs the same basic functionality as , but instead of simply unwrapping the value if it is and applying a value to generate the same default type if it is , lets you supply functions which may transform the wrapped type if it is
Okor get a default value forErr.This is kind of like a poor man's version of pattern matching, which JavaScript currently lacks.
Instead of code like this:
```ts import Result, { isOk, match } from 'true-myth/result';
const logValue = (mightBeANumber: Result<number, string>) => { console.log( mightBeANumber.isOk ? mightBeANumber.value.toString() :
There was an error: ${unsafelyGetErr(mightBeANumber)}); }; ```...we can write code like this:
```ts import Result, { match } from 'true-myth/result';
const logValue = (mightBeANumber: Result<number, string>) => { const value = match( { Ok: n => n.toString(), Err: e =>
There was an error: ${e}, }, mightBeANumber ); console.log(value); }; ```This is slightly longer to write, but clearer: the more complex the resulting expression, the hairer it is to understand the ternary. Thus, this is especially convenient for times when there is a complex result, e.g. when rendering part of a React component inline in JSX/TSX.
Parameter matcher
A lightweight object defining what to do in the case of each variant.
function or
or: { <T, E, F>(defaultResult: Result<T, F>, result: Result<T, E>): Result<T, F>; <T, E, F>(defaultResult: Result<T, F>): (result: Result<T, E>) => Result<T, F>;};Provide a fallback for a given . Behaves like a logical
or: if theresultvalue is an , returns thatresult; otherwise, returns thedefaultResultvalue.This is useful when you want to make sure that something which takes a
Resultalways ends up getting anOkvariant, by supplying a default value for the case that you currently have an .```ts import { ok, err, Result, or } from 'true-utils/result';
const okA = ok<string, string>('a'); const okB = ok<string, string>('b'); const anErr = err<string, string>(':wat:'); const anotherErr = err<string, string>(':headdesk:');
console.log(or(okB, okA).toString()); // Ok(A) console.log(or(anErr, okA).toString()); // Ok(A) console.log(or(okB, anErr).toString()); // Ok(B) console.log(or(anotherErr, anErr).toString()); // Err(:headdesk:) ```
T The type wrapped in the
Okcase ofresult. E The type wrapped in theErrcase ofresult. F The type wrapped in theErrcase ofdefaultResult.Parameter defaultResult
The
Resultto use ifresultis anErr.Parameter result
The
Resultinstance to check.Returns
resultif it is anOk, otherwisedefaultResult.
function orElse
orElse: { <T, E, R extends AnyResult>(elseFn: (err: E) => R, result: Result<T, E>): Result< T | OkFor<R>, ErrFor<R> >; <T, E, R extends AnyResult>(elseFn: (err: E) => R): ( result: Result<T, E> ) => Result<T | OkFor<R>, ErrFor<R>>;};Like , but using a function to construct the alternative .
Sometimes you need to perform an operation using the
errorvalue (and possibly other data in the environment) to construct the fallback value. In these situations, you can pass a function (which may be a closure) as theelseFnto generate the fallbackResult<T>. It can then transform the data in theErrto something usable as an , or generate a new instance as appropriate.Useful for transforming failures to usable data.
Parameter elseFn
The function to apply to the contents of the
Errifresultis anErr, to create a newResult.Parameter result
The
Resultto use if it is anOk.Returns
The
resultif it isOk, or theResultreturned byelseFnifresultis an `Err.
function safe
safe: { <F extends AnyFunction, P extends Parameters<F>, R extends ReturnType<F>>( fn: F ): (...params: P) => Result<R, unknown>; <F extends AnyFunction, P extends Parameters<F>, R extends ReturnType<F>, E>( fn: F, handleErr: (error: unknown) => E ): (...params: P) => Result<R, E>;};Transform a function which may throw an error into one with an identical call signature except that it will return a instead of throwing an error.
This allows you to handle the error locally with all the normal
Resulttools rather than having to catch an exception. Where the and functions are useful for a single call, this is useful to make a new version of a function to be used repeatedly.This overload absorbs all exceptions into an with the type
unknown. If you want to transform the error immediately rather than using a combinator, see the other overload.## Examples
The
JSON.parsemethod will throw if the string passed to it is invalid. You can use thissafemethod to transform it into a form which will *not* throw:```ts import { safe } from 'true-myth/task'; const parse = safe(JSON.parse);
let result = parse(
"ill-formed gobbledygook'); console.log(result.toString()); // Err(SyntaxError: Unterminated string in JSON at position 25) ```You could do this once in a utility module and then require that *all* JSON parsing operations in your code use this version instead.
Parameter fn
The function which may throw, which will be wrapped.
Transform a function which may throw an error into one with an identical call signature except that it will return a instead of throwing an error.
This allows you to handle the error locally with all the normal
Resulttools rather than having to catch an exception. Where the and functions are useful for a single call, this is useful to make a new version of a function to be used repeatedly.This overload allows you to transform the error immediately, using the second argument.
## Examples
The
JSON.parsemethod will throw if the string passed to it is invalid. You can use thissafemethod to transform it into a form which will *not* throw, wrapping it in a custom error :```ts import { safe } from 'true-myth/task';
class ParsingError extends Error { name = 'ParsingError'; constructor(error: unknown) { super('Parsing error.', { cause: error }); } }
const parse = safe(JSON.parse, (error) => { return new ParsingError(error); });
let result = parse(
"ill-formed gobbledygook'); console.log(result.toString()); // Err(SyntaxError: Unterminated string in JSON at position 25) ```You could do this once in a utility module and then require that *all* JSON parsing operations in your code use this version instead.
Parameter fn
The function which may throw, which will be wrapped.
Parameter handleErr
A function to use to transform an unknown error into a known error type.
function toJSON
toJSON: <T, E>(result: Result<T, E>) => Serialized<T, E>;Create an
Objectrepresentation of a instance.Useful for serialization.
JSON.stringify()uses it.Parameter result
The value to convert to JSON
Returns
The JSON representation of the
Result
function toString
toString: <T, E>(result: Result<T, E>) => string;Create a
Stringrepresentation of a instance.An instance will be
Ok(<representation of the value>), and an instance will beErr(<representation of the error>), where the representation of the value or error is simply the value or error's owntoStringrepresentation. For example:call | output --------------------------------- | ----------------------
toString(ok(42))|Ok(42)toString(ok([1, 2, 3]))|Ok(1,2,3)toString(ok({ an: 'object' }))|Ok([object Object])ntoString(err(42))|Err(42)toString(err([1, 2, 3]))|Err(1,2,3)toString(err({ an: 'object' }))|Err([object Object])T The type of the wrapped value; its own
.toStringwill be used to print the interior contents of theJustvariant.Parameter result
The value to convert to a string.
Returns
The string representation of the
Maybe.
function transposeAll
transposeAll: (results: readonly []) => Result<[], never>;Given an array of results, return a new result if all results are or a new result with all errors if one or more of the results is .
## Examples
If all results are , return a new result containing an array of all provided values:
```ts import Result, { transposeAll } from 'true-myth/result';
let result = transposeAll([ Result.ok(10), Result.ok(100), Result.ok(1000) ]);
console.log(result.toString()); // Ok(10,100,1000) ```
If any result is , return a new result containing an array of all encountered:
```ts import Result, { transposeAll } from 'true-myth/result';
let result = transposeAll([ Result.ok(10), Result.err("something went wrong"), Result.err("something else went wrong") ]);
console.log(result.toString()); // Err(something went wrong,something else went wrong) ```
Parameter results
The list of results. A new containing an array of all values if all results are , or a new result containing an array of all encountered.
A The type of the array or tuple of results.
function transposeAny
transposeAny: (results: []) => Result<[], never>;Given an array of results, return a new result if _any_ of the results is , or a new result if _all_ the results are .
## Examples
When any result is , return a new result containing the value of the first result:
```ts import { any } from 'true-myth/result';
let result = any([ Result.err("something went wrong"), Result.ok(10), Result.err("something else went wrong") ]);
console.log(result.toString()); // Ok(10); ```
When all results are , return a new result containing an array of all encountered:
```ts import { any } from 'true-myth/result';
let result = any([ Result.err("something went wrong"), Result.err("something else went wrong"), Result.err("even more went wrong") ]);
console.log(result.toString()); // Err(something went wrong,something else went wrong,even more went wrong) ```
Parameter results
The list of results to check.
Returns
A new containing the value of the first result if any results are , or a new result containing an array of all encountered if all results are .
A The type of the array or tuple of tasks.
function tryOr
tryOr: { <T, E>(error: E, callback: () => T): Result<T, E>; <T, E>(error: E): (callback: () => T) => Result<T, E>;};Execute the provided callback, wrapping the return value in or if there is an exception.
```ts const aSuccessfulOperation = () => 2 + 2;
const anOkResult = Result.tryOr('Oh noes!!1', () => { aSuccessfulOperation() }); // => Ok(4)
const thisOperationThrows = () => throw new Error('Bummer');
const anErrResult = Result.tryOr('Oh noes!!1', () => { thisOperationThrows(); }); // => Err('Oh noes!!1') ```
Parameter error
The error value in case of an exception
Parameter callback
The callback to try executing
function tryOrElse
tryOrElse: { <T, E>(onError: (e: unknown) => E, callback: () => T): Result<T, E>; <T, E>(onError: (e: unknown) => E): (callback: () => T) => Result<T, E>;};Execute the provided callback, wrapping the return value in . If there is an exception, return a of whatever the
onErrorfunction returns.```ts import { tryOrElse } from 'true-myth/result';
const aSuccessfulOperation = () => 2 + 2;
const anOkResult = tryOrElse( (e) => e, aSuccessfulOperation ); // => Ok(4)
const thisOperationThrows = () => throw 'Bummer'
const anErrResult = tryOrElse( (e) => e, () => { thisOperationThrows(); } ); // => Err('Bummer') ```
Parameter onError
A function that takes
eexception and returns what will be wrapped in aResult.ErrParameter callback
The callback to try executing
function unwrapOr
unwrapOr: { <T, U, E>(defaultValue: U, result: Result<T, E>): U | T; <T, U, E>(defaultValue: U): (result: Result<T, E>) => T | U;};Safely get the value out of the variant of a .
This is the recommended way to get a value out of a
Resultmost of the time.```ts import { ok, err, unwrapOr } from 'true-myth/result';
const anOk = ok<number, string>(12); console.log(unwrapOr(0, anOk)); // 12
const anErr = err<number, string>('nooooo'); console.log(unwrapOr(0, anErr)); // 0 ```
T The value wrapped in the
Ok. E The value wrapped in theErr.Parameter defaultValue
The value to use if
resultis anErr.Parameter result
The
Resultinstance to unwrap if it is anOk.Returns
The content of
resultif it is anOk, otherwisedefaultValue.
function unwrapOrElse
unwrapOrElse: { <T, U, E>(orElseFn: (error: E) => U, result: Result<T, E>): T | U; <T, U, E>(orElseFn: (error: E) => U): (result: Result<T, E>) => T | U;};Safely get the value out of a by returning the wrapped value if it is , or by applying
orElseFnto the value in the .This is useful when you need to *generate* a value (e.g. by using current values in the environment – whether preloaded or by local closure) instead of having a single default value available (as in ).
```ts import { ok, err, unwrapOrElse } from 'true-myth/result';
// You can imagine that someOtherValue might be dynamic. const someOtherValue = 2; const handleErr = (errValue: string) => errValue.length + someOtherValue;
const anOk = ok<number, string>(42); console.log(unwrapOrElse(handleErr, anOk)); // 42
const anErr = err<number, string>('oh teh noes'); console.log(unwrapOrElse(handleErr, anErr)); // 13 ```
T The value wrapped in the
Ok. E The value wrapped in theErr.Parameter orElseFn
A function applied to the value wrapped in
resultif it is anErr, to generate the final value.Parameter result
The
resultto unwrap if it is anOk.Returns
The value wrapped in
resultif it isOkor the value returned byorElseFnapplied to the value inErr.
interface Err
interface Err<T, E> extends Omit<ResultImpl<T, E>, 'value' | 'cast'> {}An
Errinstance is the *failure* variant instance of the type, representing a failure outcome from an operation which may fail. For a full discussion, see the module docs.T The type which would be wrapped in an
Okvariant ofResult. E The type wrapped in thisErrvariant ofResult.
interface ErrJSON
interface ErrJSON<E> {}Representation of an when serialized to JSON.
interface Ok
interface Ok<T, E> extends Omit<ResultImpl<T, E>, 'error' | 'cast'> {}An
Okinstance is the *successful* variant instance of the type, representing a successful outcome from an operation which may fail. For a full discussion, see the module docs.T The type wrapped in this
Okvariant ofResult. E The type which would be wrapped in anErrvariant ofResult.
interface OkJSON
interface OkJSON<T> {}Representation of an when serialized to JSON.
interface ResultConstructor
interface ResultConstructor {}The public interface for the class *as a value*: the static constructors
okanderrproduce aResultwith that variant.
type AnyResult
type AnyResult = Result<unknown, unknown>;A convenient way to name
Result<unknown, unknown>.
type Matcher
type Matcher<T, E, A> = { /** Transform a `T` into the resulting type `A`. */ Ok: (value: T) => A; /** Transform an `E` into the resulting type `A`. */ Err: (error: E) => A;};A lightweight object defining how to handle each variant of a .
T The success type E The error type A The type resulting from calling on a
type Result
type Result<T, E> = Ok<T, E> | Err<T, E>;A
Resultrepresents success () or failure ().The behavior of this type is checked by TypeScript at compile time, and bears no runtime overhead other than the very small cost of the container object.
type ResultJSON
type ResultJSON<T, E> = OkJSON<T> | ErrJSON<E>;Representation of a when serialized to JSON.
type Serialized
type Serialized<T, E> = ResultJSON<FlattenedJSON<T>, FlattenedJSON<E>>;Representation of a possibly-nested when serialized.
type SomeResult
type SomeResult<T, E> = { [IsResult]: [T, E];};type Variant
type Variant = keyof typeof Variant;namespace standardSchema
module 'dist/standard-schema.d.ts' {}Provides useful integrations between True Myth’s and types and any library that implements [Standard Schema][ss].
[ss]: https://standardschema.dev
function asyncParserFor
asyncParserFor: <S extends StandardSchemaV1<unknown, unknown>>( schema: S) => AsyncParserFor<StandardSchemaV1.InferOutput<S>>;Create an asynchronous parser for unknown data to use with any library that implements Standard Schema (Zod, Arktype, Valibot, etc.).
The resulting parser will accept
unknowndata and emit a , which will be if the schema successfully validates, or with the s generated by the schema for invalid data.If passed a parser that produces results synchronously, this function will lift it into a .
## Examples
With Zod:
```ts import { asyncParserFor } from 'true-myth/standard-schema'; import * as z from 'zod';
interface Person { name?: string | undefined; age: number; }
const parsePerson = asyncParserFor(z.object({ name: z.optional(z.string()), // Define an async refinement so we have something to work with. This is a // placeholder for some kind of *real* async validation you might do! age: z.number().refine(async (val) => val >= 0), })); ```
Other libraries that support async validation or transformation work similarly (but not all libraries support this).
Once you have a parser, you can simply call it with any value and then use the normal APIs.
```ts await parsePerson({ name: "Old!", age: 112 }).match({ Resolved: (person) => { console.log(
${person.name ?? "someone"} is ${person.age} years old.); }, Rejected: (error) => { console.error("Something is wrong!", ...error.issues); } }); ```Parameter schema
A Standard Schema-compatible schema that produces a result, possibly asynchronously.
Returns
A that resolves to the output of the schema when it parses successfully and rejects with the
StandardSchemaFailureResultwhen it fails to parse.
function parserFor
parserFor: <S extends StandardSchemaV1<unknown, unknown>>( schema: S) => ParserFor<StandardSchemaV1.InferOutput<S>>;Create a synchronous parser for unknown data to use with any library that implements Standard Schema (Zod, Arktype, Valibot, etc.).
The resulting parser will accept
unknowndata and emit a , which will be if the schema successfully validates, or a with the s generated by the schema for invalid data.## Examples
Creating a parser with Zod:
```ts import { parserFor } from 'true-myth/standard-schema'; import * as z from 'zod';
interface Person { name?: string | undefined; age: number; }
const parsePerson = parserFor(z.object({ name: z.string().optional(), age: z.number().nonnegative(), })); ```
Creating a parser with Arktype:
```ts import { parserFor } from 'true-myth/standard-schema'; import { type } from 'arktype';
interface Person { name?: string | undefined; age: number; }
const parsePerson = parserFor(type({ 'name?': 'string', age: 'number>=0', })); ```
Other libraries work similarly!
Once you have a parser, you can simply call it with any value and then use the normal APIs.
```ts parsePerson({ name: "Old!", age: 112 }).match({ Ok: (person) => { console.log(
${person.name ?? "someone"} is ${person.age} years old.); }, Err: (error) => { console.error("Something is wrong!", ...error.issues); } }); ```## Throws
The parser created by
parserForwill throw an error if the schema it was created from produces an async result, i.e., aPromise. Standard Schema is [currently unable][gh] to distinguish between synchronous and asynchronous parsers due to limitations in Zod.If you need to handle schemas which may throw, use instead. It will safely lift *all* results into a , which you can then safely interact with asynchronously as usual.
[gh]: https://github.com/standard-schema/standard-schema/issues/22
class InvalidAsyncSchema
class InvalidAsyncSchema extends Error {}An error thrown when calling a parser created with
parserForproduces aPromiseinstance.
constructor
constructor();property name
readonly name: string;interface StandardSchemaV1
interface StandardSchemaV1<Input = unknown, Output = Input> {}The Standard Schema interface.
property '~standard'
readonly '~standard': StandardSchemaV1.Props<Input, Output>;The Standard Schema properties.
type AsyncParserFor
type AsyncParserFor<T> = (data: unknown) => ParseTask<T>;A type to name an async parser, most often used in conjunction with . Helpful if you want to use a type you have written to constrain a Standard Schema-compatible async schema, instead of creating the type from the schema.
## Example
```ts import { parserFor, type ParserFor } from 'true-myth/standard-schema'; import { type } from 'arktype';
interface Person { name?: string | undefined; age: number; }
const personParser: ParserFor = parserFor(type({ "name?": "string", age: "number>=0", })); ```
type ParseResult
type ParseResult<T> = Result<T, StandardSchemaV1.FailureResult>;The result of parsing data with a synchronous Standard Schema-compliant schema. Just a whose failure type is always a Standard Schema
FailureResult.
type ParserFor
type ParserFor<T> = (data: unknown) => ParseResult<T>;A type to name a parser, most often used in conjunction with . Helpful if you want to use a type you have written to constrain a Standard Schema-compatible schema, instead of creating the type from the schema.
## Example
```ts import { parserFor, type ParserFor } from 'true-myth/standard-schema'; import * as z from 'zod';
interface Person { name?: string | undefined; age: number; }
const personParser: ParserFor = parserFor(z.object({ name: z.string().optional(), age: z.number().nonnegative(), })); ```
type ParseTask
type ParseTask<T> = Task<T, StandardSchemaV1.FailureResult>;The result of parsing data with an asynchronous Standard Schema-compliant schema. Just a whose rejection type is always a Standard Schema
FailureResult.
namespace standardSchema.StandardSchemaV1
namespace standardSchema.StandardSchemaV1 {}interface FailureResult
interface FailureResult {}The result interface if validation fails.
property issues
readonly issues: ReadonlyArray<Issue>;The issues of failed validation.
interface Issue
interface Issue {}The issue interface of the failure output.
interface PathSegment
interface PathSegment {}The path segment interface of the issue.
property key
readonly key: PropertyKey;The key representing a path segment.
interface Props
interface Props<Input = unknown, Output = Input> {}The Standard Schema properties interface.
property types
readonly types?: Types<Input, Output> | undefined;Inferred types associated with the schema.
property validate
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;Validates unknown input values.
property vendor
readonly vendor: string;The vendor name of the schema library.
property version
readonly version: 1;The version number of the standard.
interface SuccessResult
interface SuccessResult<Output> {}The result interface if validation succeeds.
interface Types
interface Types<Input = unknown, Output = Input> {}The Standard Schema types interface.
type InferInput
type InferInput<Schema extends StandardSchemaV1> = NonNullable< Schema['~standard']['types']>['input'];Infers the input type of a Standard Schema.
type InferOutput
type InferOutput<Schema extends StandardSchemaV1> = NonNullable< Schema['~standard']['types']>['output'];Infers the output type of a Standard Schema.
type Result
type Result<Output> = SuccessResult<Output> | FailureResult;The result interface of the validate function.
namespace task
module 'dist/task.d.ts' {}A is a type representing an asynchronous operation that may fail, with a successful (“resolved”) value of type
Tand an error (“rejected”) value of typeE.If the
Taskis pending, it is . If it has resolved, it is . If it has rejected, it is .For more, see [the guide](/guide/understanding/task/).
variable []
const []: any;variable []
const []: any;variable []
const []: any;variable A
const A: any;variable A
const A: any;variable A
const A: any;variable AnyTask
const AnyTask: any;variable AnyTask
const AnyTask: any;variable AnyTask
const AnyTask: any;variable IsTask
const IsTask: Symbol;variable PhantomData
const PhantomData: Symbol;variable readonly
const readonly: any;variable readonly
const readonly: any;variable readonly
const readonly: any;variable reject
const reject: { <T = never, E extends {} = {}>(): Task<T, Unit>; <T = never, E = unknown>(reason: E): Task<T, E>;};Standalone function version of
variable resolve
const resolve: { <T extends Unit, E = never>(): Task<Unit, E>; <T, E = never>(value: T): Task<T, E>;};Standalone function version of
variable RETRY_FAILED_NAME
const RETRY_FAILED_NAME: string;variable safelyTryOr
const safelyTryOr: { <T, E>(rejection: E, fn: () => Promise<T>): Task<T, E>; <T, E>(rejection: E): (fn: () => Promise<T>) => Task<T, E>;};An alias for for ease of migrating from v8.x to v9.x.
> [!TIP] > You should switch to . We expect to deprecate and remove > this alias at some point!
variable safelyTryOrElse
const safelyTryOrElse: { <T, E>(onError: (reason: unknown) => E, fn: () => PromiseLike<T>): Task<T, E>; <T, E>(onError: (reason: unknown) => E): ( fn: () => PromiseLike<T> ) => Task<T, E>;};An alias for for ease of migrating from v8.x to v9.x.
> [!TIP] > You should switch to . We expect to deprecate and > remove this alias at some point!
variable State
const State: { readonly Pending: 'Pending'; readonly Resolved: 'Resolved'; readonly Rejected: 'Rejected';};variable Task
const Task: TaskConstructor;A
Taskis a type safe asynchronous computation.You can think of a
Task<T, E>as being basically aPromise<Result<T, E>>, because it *is* aPromise<Result<T, E>>under the hood, but with two main differences from a “normal”Promise:1. A
Task*cannot* “reject”. All errors must be handled. This means that, like a , it will *never* throw an error if used in strict TypeScript.2. Unlike
Promise,Taskrobustly distinguishes betweenmapandandThenoperations.Taskalso implements JavaScript’sPromiseLikeinterface, so you canawaitit; when aTask<T, E>is awaited, it produces a .
function all
all: (tasks: []) => Task<[], never>;Given an array of tasks, return a new
Taskthat resolves once all tasks successfully resolve or any task rejects.## Examples
Once all tasks resolve:
```ts import { all, timer } from 'true-myth/task';
let allTasks = all([ timer(10), timer(100), timer(1_000), ]);
let result = await allTasks; console.log(result.toString()); // [Ok(10,100,1000)] ```
If any tasks do *not* resolve:
```ts let { task: willReject, reject } = Task.withResolvers<never, string>();
let allTasks = all([ timer(10), timer(20), willReject, ]);
reject("something went wrong"); let result = await allTasks; console.log(result.toString()); // Err("something went wrong") ```
Parameter tasks
The list of tasks to wait on.
A The type of the array or tuple of tasks.
function allSettled
allSettled: () => any;Given an array of tasks, return a new which resolves once all of the tasks have either resolved or rejected. The resulting
Taskis a tuple or array corresponding exactly to the tasks passed in, either resolved or rejected.## Example
Given a mix of resolving and rejecting tasks:
```ts let settledTask = allSettled([ Task.resolve<string, number>("hello"), Task.reject<number, boolean>(true), Task.resolve<{ fancy: boolean }>, Error>({ fancy: true }), ]);
let output = await settledTask; if (output.isOk) { // always true, not currently statically knowable for (let result of output.value) { console.log(result.toString()); } } ```
The resulting output will be:
``` Ok("hello"), Err(true), Ok({ fancy: true }), ```
Parameter tasks
The tasks to wait on settling.
A The type of the array or tuple of tasks.
function and
and: { <T, U, E>(andTask: Task<U, E>): (task: Task<T, E>) => Task<U, E>; <T, U, E>(andTask: Task<U, E>, task: Task<T, E>): Task<U, E>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function andThen
andThen: { <T, E, R extends AnyResult | AnyTask>(thenFn: (t: T) => R): ( task: Task<T, E> ) => Task<ResolvesTo<R>, E | RejectsWith<R>>; <T, U, E>(thenFn: (t: T) => U): (task: Task<T, E>) => Task<U, E>; <T, E, R extends AnyResult | AnyTask>( thenFn: (t: T) => R, task: Task<T, E> ): Task<ResolvesTo<R>, E | RejectsWith<R>>; <T, U, E>(thenFn: (t: T) => Task<U, E> | Result<U, E>, task: Task<T, E>): Task< U, E >;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function any
any: (tasks: []) => Task<never, AggregateRejection<[]>>;Given an array of tasks, return a new which resolves once _any_ of the tasks resolves successfully, or which rejects once _all_ the tasks have rejected.
## Examples
When any item resolves:
```ts import { any, timer } from 'true-myth/task';
let anyTask = any([ timer(20), timer(10), timer(30), ]);
let result = await anyTask; console.log(result.toString()); // Ok(10); ```
When all items reject:
```ts import Task, { timer } from 'true-myth/task';
let anyTask = any([ timer(20).andThen((time) => Task.reject(
${time}ms)), timer(10).andThen((time) => Task.reject(${time}ms)), timer(30).andThen((time) => Task.reject(${time}ms)), ]);let result = await anyTask; console.log(result.toString()); // Err(AggregateRejection:
Task.any: 10ms,20ms,30ms) ```The order in the resulting
AggregateRejectionis guaranteed to be stable and to match the order of the tasks passed in.Parameter tasks
The set of tasks to check for any resolution.
Returns
A Task which is either with the value of the first task to resolve, or with the rejection reasons for all the tasks passed in in an . Note that the order of the rejection reasons is not guaranteed.
A The type of the array or tuple of tasks.
function flatten
flatten: <T, E, F>(nestedTask: Task<Task<T, E>, F>) => Task<T, E | F>;Given a nested
Task, remove one layer of nesting.For example, given a
Task<Task<string, E2>, E1>, the resulting type after using this function will beTask<string, E1 | E2>.## Note
This function only works when the value wrapped in
Taskis anotherTask. If you have aTask<string, E>orTask<number, E>, this function won't work. If you have aTask<Task<string, E2>, E1>, then you can call.flatten()to get back aTask<string, E1 | E2>.## Examples
```ts import * as task from 'true-myth/task';
const nested = task.resolve(task.resolve('hello')); const flattened = task.flatten(nested); // Task<string, never> await flattened; console.log(flattened); //
Resolved('hello')const nestedError = task.resolve(task.reject('inner error')); const flattenedError = task.flatten(nestedError); // Task<never, string> await flattenedError; console.log(flattenedError); //
Rejected('inner error')const errorNested = task.reject<Task<string, string>, string>('outer error'); const flattenedOuter = task.flatten(errorNested); // Task<string, string> await flattenedOuter; console.log(flattenedOuter); //
Rejected('outer error')```
function fromPromise
fromPromise: { <T>(promise: Promise<T>): Task<T, unknown>; <T, E>(promise: Promise<T>, onRejection: (reason: unknown) => E): Task<T, E>;};Produce a from a [
Promise][mdn-promise].[mdn-promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
To handle the error case and produce a
Task<T, E>instead, use the overload the overload which accepts anonRejectionhandler instead.> [!IMPORTANT] > This does not (and by definition cannot) handle errors that happen during > construction of the
Promise, because those happen before this is called. > See , , or > for alternatives which accept a callback for > constructing a promise and can therefore handle errors thrown in the call.Parameter promise
The promise from which to create the
Task.T The type the
Promisewould resolve to, and thus that theTaskwill also resolve to if thePromiseresolves.Constructors
Produce a from a [
Promise][mdn-promise], using a .[mdn-promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
To absorb all errors/rejections as
unknown, use the overload without anonRejectionhandler instead.> [!IMPORTANT] > This does not (and by definition cannot) handle errors that happen during > construction of the
Promise, because those happen before this is called. > See , , or > for alternatives which accept a callback for > constructing a promise and can therefore handle errors thrown in the call.Parameter promise
The promise from which to create the
Task.Parameter onRejection
Transform errors from
unknownto a known error type.T The type the
Promisewould resolve to, and thus that theTaskwill also resolve to if thePromiseresolves. E The type of a rejectedTaskif the promise rejects.Constructors
function fromResult
fromResult: <T, E>(result: Result<T, E>) => Task<T, E>;Build a from a .
> [!IMPORTANT] > This does not (and by definition cannot) handle errors that happen during > construction of the
Result, because those happen before this is called. > See and as well as the corresponding > and methods for synchronous functions.## Examples
Given an ,
fromResultwill produces a task.```ts import { fromResult } from 'true-myth/task'; import { ok } from 'true-myth/result';
let successful = fromResult(ok("hello")); // -> Resolved("hello") ```
Likewise, given an
Err,fromResultwill produces a task.```ts import { fromResult } from 'true-myth/task'; import { err } from 'true-myth/result';
let successful = fromResult(err("uh oh!")); // -> Rejected("uh oh!") ```
It is often clearest to access the function via a namespace-style import:
```ts
import * as task from 'true-myth/task'; import { ok } from 'true-myth/result';
let theTask = task.fromResult(ok(123)); ```
As an alternative, it can be useful to rename the import:
```ts import { fromResult: taskFromResult } from 'true-myth/task'; import { err } from 'true-myth/result';
let theTask = taskFromResult(err("oh no!")); ```
function fromUnsafePromise
fromUnsafePromise: <T, E>(promise: Promise<Result<T, E>>) => Task<T, E>;Produce a
Task<T, E>from a promise of a .> [!WARNING] > This constructor assumes you have already correctly handled the promise > rejection state, presumably by mapping it into the wrapped
Result. It is > *unsafe* for this promise ever to reject! You should only ever use this > withPromise<Result<T, E>>you have created yourself (including via a >Task, of course). > > For any otherPromise<Result<T, E>>, you should first attach acatch> handler which will also produce aResult<T, E>. > > If you call this with an unmanagedPromise<Result<T, E>>, that is, one > that has *not* correctly set up acatchhandler, the rejection will > throw an error that will ***not*** be catchable > by awaiting theTaskor its originalPromise. This can cause test > instability and unpredictable behavior in your application.Parameter promise
The promise from which to create the
Task.Constructors
function inspect
inspect: { <T, E>(fn: (value: T) => void): (task: Task<T, E>) => Task<T, E>; <T, E>(fn: (value: T) => void, task: Task<T, E>): Task<T, E>;};Auto-curried, standalone function form of .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Taskresolves, and the originalTaskis returned unchanged for further chaining.> [!NOTE] > TypeScript type inference is limited with the curried form. You will need to > provide explicit type parameters. Alternatively, use the non-curried form > to get consistent type inference.
```ts import * as task from 'true-myth/task';
const double = (n: number) => n * 2; const log = (value: unknown) => console.log(value);
const logNumber = task.inspect<number, unknown>(log); const doubleTask = task.map(double);
// Logs
42then84, and returnsOk(84). let aResolvingTask = task.resolve<number, string>(42); let aResolvingTask = task.resolve<number, string>(42); await logNumber(doubleTask(logNumber(aResolvingTask)));// Does not log anything, and returns
Err('error'). let aRejectingTask = task.reject<number, string>('error'); await logNumber(doubleTask(logNumber(aRejectingTask))); ```T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Parameter fn
The function to call with the resolved value (only called on resolution)
Parameter task
The Task to inspect
Returns
The original Task, unchanged
Standalone function form of .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Taskresolves, and the originalTaskis returned unchanged for further chaining.```ts import * as task from 'true-myth/task';
const double = (n: number) => n * 2; const log = (value: unknown) => console.log(value);
// Logs
42then84, and returnsResolved(84). let aResolvingTask = task.resolve<number, string>(42); task.inspect( log, task.map( double, task.inspect( log, aResolvingTask ) ) );// Does not log anything, and returns
Err('error'). let aRejectingTask = task.reject<number, string>('error'); await task.inspect( log, task.map( double, task.inspect( log, aRejectingTask ) ) ); ```T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Parameter fn
The function to call with the resolved value (only called on resolution)
Parameter task
The Task to inspect
Returns
The original Task, unchanged
function inspectRejected
inspectRejected: { <T, E>(fn: (error: E) => void): (task: Task<T, E>) => Task<T, E>; <T, E>(fn: (error: E) => void, task: Task<T, E>): Task<T, E>;};Auto-curried, standalone function form of .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Taskreject, and the originalTaskis returned unchanged for further chaining.> [!NOTE] > TypeScript type inference is limited with the curried form. You will need to > provide explicit type parameters. Alternatively, use the non-curried form > to get consistent type inference.
```ts import * as task from 'true-myth/task';
const double = (n: number) => n * 2; const logError = (error: unknown) => console.error('Got error:', error);
const logAnyError = task.inspect<number, unknown>(logError); const doubleTask = task.map(double);
// Does not log anything, and returns
Resolved(42). let aResolvingTask = task.resolve<number, string>(42); logAnyError(doubleTask(logAnyError(aResolvingTask)));// Logs: "Got error: error" let aRejectingTask = task.reject<number, string>('error'); logAnyError(doubleTask(logAnyError(aRejectingTask))); ```
T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Parameter fn
The function to call with the rejection reason (only called on rejection)
Parameter task
The Task to inspect
Returns
The original Task, unchanged
Standalone function form of .
This is useful for performing actions like logging, debugging, or other “side effects” external to the wrapped value. (**Note:** You should *never* mutate the value in the callback. Doing so will be extremely surprising to callers.) The function is only called if the
Taskreject, and the originalTaskis returned unchanged for further chaining.```ts import * as task from 'true-myth/task';
const double = (n: number) => n * 2; const logError = (error: unknown) => console.error('Got error:', error);
// Does not log anything, and returns
Resolved(42). let aResolvingTask = task.resolve<number, string>(42); task.inspectRejected( logError, task.map( double, task.inspectRejected( logError, aResolvingTask ) ) );// Logs: "Got error: error" let aRejectingTask = task.reject<number, string>('error'); task.inspectRejected( logError, task.map( double, task.inspectRejected( logError, aRejectingTask ) ) ); ```
T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Parameter fn
The function to call with the rejection reason (only called on rejection)
Parameter task
The Task to inspect
Returns
The original Task, unchanged
function isRetryFailed
isRetryFailed: (error: unknown) => error is RetryFailed<unknown>;function map
map: { <T, U, E>(mapFn: (t: T) => U): (task: Task<T, E>) => Task<U, E>; <T, U, E>(mapFn: (t: T) => U, task: Task<T, E>): Task<U, E>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function mapRejected
mapRejected: { <T, E, F>(mapFn: (e: E) => F): (task: Task<T, E>) => Task<T, F>; <T, E, F>(mapFn: (e: E) => F, task: Task<T, E>): Task<T, F>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Standalone function form of .
T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function match
match: { <T, E, A>(matcher: Matcher<T, E, A>): (task: Task<T, E>) => Promise<A>; <T, E, A>(matcher: Matcher<T, E, A>, task: Task<T, E>): Promise<A>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function or
or: { <U, F, T, E>(other: Task<U, F>): (task: Task<T, E>) => Task<T | U, F>; <U, F, T, E>(other: Task<U, F>, task: Task<T, E>): Task<U | T, F>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function orElse
orElse: { <T, E, F, U = T>(elseFn: (reason: E) => Task<U, F>): ( task: Task<T, E> ) => Task<T | U, F>; <T, E, R extends AnyTask>(elseFn: (reason: E) => R): ( task: Task<T, E> ) => Task<T | ResolvesTo<R>, RejectsWith<R>>; <T, E, F, U = T>(elseFn: (reason: E) => Task<U, F>, task: Task<T, E>): Task< T | U, F >; <T, E, R extends AnyTask>(elseFn: (reason: E) => R, task: Task<T, E>): Task< T | ResolvesTo<R>, RejectsWith<R> >;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function race
race: { (tasks: []): Task<never, never>; <A extends readonly AnyTask[]>(tasks: A): Task< { -readonly [P in keyof A]: ResolvesTo<A[P]> }[number], { -readonly [P in keyof A]: RejectsWith<A[P]> }[number] >;};Given an array of tasks, produce a new which will resolve or reject with the resolution or rejection of the *first* task which settles.
## Example
```ts import Task, { race } from 'true-myth/task';
let { task: task1, resolve } = Task.withResolvers(); let task2 = new Task((_resolve) => {}); let task3 = new Task((_resolve) => {});
resolve("Cool!"); let theResult = await race([task1, task2, task3]); console.log(theResult.toString()); // Ok("Cool!") ```
Parameter tasks
The tasks to race against each other.
A The type of the array or tuple of tasks.
function safe
safe: { < F extends (...params: never[]) => PromiseLike<unknown>, P extends Parameters<F>, R extends Awaited<ReturnType<F>> >( fn: F ): (...params: P) => Task<R, unknown>; < F extends (...params: never[]) => PromiseLike<unknown>, P extends Parameters<F>, R extends Awaited<ReturnType<F>>, E >( fn: F, onError: (reason: unknown) => E ): (...params: P) => Task<R, E>;};Given a function which returns a
Promise, return a new function with the same parameters but which returns a instead.If you wish to transform the error directly, rather than with a combinator, see the other overload, which accepts an error handler.
## Examples
You can use this to create a safe version of the
fetchfunction, which will produce aTaskinstead of aPromiseand which does not throw an error for rejections, but instead produces a variant of theTask.```ts import { safe } from 'true-myth/task';
const fetch = safe(window.fetch); const toJson = safe((response: Response) => response.json() as unknown); let json = fetch('https://www.example.com/api/users').andThen(toJson); ```
Parameter fn
A function to wrap so it never throws an error or produces a
Promiserejection.Given a function which returns a
Promiseand a function to transform thrown errors orPromiserejections resulting from calling that function, return a new function with the same parameters but which returns a .To catch all errors but leave them unhandled and
unknown, see the other overload.## Examples
You can use this to create a safe version of the
fetchfunction, which will produce aTaskinstead of aPromiseand which does not throw an error for rejections, but instead produces a variant of theTask.```ts import { safe } from 'true-myth/task';
class CustomError extends Error { constructor(name: string, cause: unknown) { super(
my-lib.error.${name}, { cause }); this.name = name; } }function handleErr(name: string): (cause: unknown) => CustomError { return (cause) => new CustomError(name); }
const fetch = safe(window.fetch, handleErr('fetch')); const toJson = safe( (response: Response) => response.toJson(), handleErr('json-parsing') );
let json = fetch('https://www.example.com/api/users').andThen(toJson); ```
Parameter fn
A function to wrap so it never throws an error or produces a
Promiserejection.Parameter onError
A function to use to transform the
function safelyTry
safelyTry: <T>(fn: () => Promise<T>) => Task<T, unknown>;Given a function which takes no arguments and returns a
Promise, return a for the result of invoking that function. This safely handles functions which fail synchronously or asynchronously, so unlike is safe to use with values which may throw errors _before_ producing aPromise.## Examples
```ts import { safelyTry } from 'true-myth/task';
function throws(): Promise { throw new Error("Uh oh!"); }
// Note: passing the function by name, *not* calling it. let theTask = safelyTry(throws); let theResult = await theTask; console.log(theResult.toString()); // Err(Error: Uh oh!) ```
Parameter fn
A function which returns a
Promisewhen called.Returns
A
Taskwhich resolves to the resolution value of the promise or rejects with the rejection value of the promise *or* any error thrown while invokingfn.
function safeNullable
safeNullable: { < F extends (...params: never[]) => PromiseLike<unknown>, P extends Parameters<F>, R extends Awaited<ReturnType<F>> >( fn: F ): (...params: P) => Task<Maybe<NonNullable<R>>, unknown>; < F extends (...params: never[]) => PromiseLike<unknown>, P extends Parameters<F>, R extends Awaited<ReturnType<F>>, E >( fn: F, onError: (reason: unknown) => E ): (...params: P) => Task<Maybe<NonNullable<R>>, E>;};Given a function which returns a
Promiseof a nullable type, return a new function with the same parameters but which returns a of a instead.If you wish to transform the error directly, rather than with a combinator, see the other overload, which accepts an error handler.
This is basically just a convenience for something you could do yourself by chaining
safewithMaybe.of:```ts import Maybe from 'true-myth/maybe'; import { safe, safeNullable } from 'true-myth/task';
async function numberOrNull(value: number): Promise<number | null> { return Math.random() > 0.5 ? value : null; }
// Using this helper const safeNumberOrNull = safeNullable(numberOrNull);
// Using
safeandMaybe.ofmanually const moreWorkThisWay= safe(numberOrNull); let theTask = moreWorkThisWay(123).map((n) => Maybe.of(n)); ```The convenience is high, though, since you can now use this to create fully safe abstractions to use throughout your codebase, rather than having to remember to do the additional call to
maptheTask’s resolution value into aMaybeat each call site.Parameter fn
A function to wrap so it never throws an error or produces a
Promiserejection.Given a function which returns a
Promiseand a function to transform thrown errors orPromiserejections resulting from calling that function, return a new function with the same parameters but which returns a .To catch all errors but leave them unhandled and
unknown, see the other overload.This is basically just a convenience for something you could do yourself by chaining
safewithMaybe.of:```ts import Maybe from 'true-myth/maybe'; import { safe, safeNullable } from 'true-myth/task';
async function numberOrNull(value: number): Promise<number | null> { return Math.random() > 0.5 ? value : null; }
// Using this helper const safeNumberOrNull = safeNullable(numberOrNull);
// Using
safeandMaybe.ofmanually const moreWorkThisWay= safe(numberOrNull); let theTask = moreWorkThisWay(123).map((n) => Maybe.of(n)); ```The convenience is high, though, since you can now use this to create fully safe abstractions to use throughout your codebase, rather than having to remember to do the additional call to
maptheTask’s resolution value into aMaybeat each call site.Parameter fn
A function to wrap so it never throws an error or produces a
Promiserejection.Parameter onError
A function to use to transform the
function stopRetrying
stopRetrying: (message: string, cause?: unknown) => StopRetrying;Produces the “sentinel”
Errorsubclass , for use as a return value from .Parameter message
The message to attach to the instance.
Parameter cause
The previous cause (often another
Error) that resulted in stopping retries.
function timeout
timeout: { <T, E>(timerOrMs: Timer | number): (task: Task<T, E>) => Task<T, E | Timeout>; <T, E>(timerOrMs: number | Timer, task: Task<T, E>): Task<T, E | Timeout>;};Auto-curried, standalone function form of .
> [!TIP] > The auto-curried version is provided for parity with the similar functions > that the
MaybeandResultmodules provide. However, likeResult, you > will likely find that this form is somewhat difficult to use, because > TypeScript’s type inference does not support it well: you will tend to end > up with an awful lot ofunknownunless you write the type parameters > explicitly at the call site. > > The non-curried form will not have that problem, so you should prefer it.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function timer
timer: (ms: number) => Timer;Create a which will resolve to the number of milliseconds the timer waited for that time elapses. (In other words, it safely wraps the [
setTimeout][setTimeout] function.)[setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
This can be used as a “timeout” by calling it in conjunction any of the helpers like , , and so on. As a convenience to use it as a timeout for another task, you can also combine it with the instance method or the standalone function.
Provides the requested duration of the timer in case it is useful for working with multiple timers.
Parameter ms
The number of milliseconds to wait before resolving the
Task.Returns
a Task which resolves to the passed-in number of milliseconds.
function toPromise
toPromise: <T, E>(task: Task<T, E>) => Promise<Result<T, E>>;Standalone version of .
T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
function tryOr
tryOr: { <T, E>(rejection: E, fn: () => Promise<T>): Task<T, E>; <T, E>(rejection: E): (fn: () => Promise<T>) => Task<T, E>;};Given a function which takes no arguments and returns a
Promiseand a value of typeEto use as the rejection if thePromiserejects, return a for the result of invoking that function. This safely handles functions which fail synchronously or asynchronously, so unlike is safe to use with values which may throw errors _before_ producing aPromise.## Examples
```ts import { tryOr } from 'true-myth/task';
function throws(): Promise { throw new Error("Uh oh!"); }
// Note: passing the function by name, *not* calling it. let theTask = tryOr("fallback", throws); let theResult = await theTask; if (theResult.isErr) { console.error(theResult.error); // "fallback" } ```
You can also write this in “curried” form, passing just the fallback value and getting back a function which accepts the:
```ts import { tryOr } from 'true-myth/task';
function throws(): Promise { throw new Error("Uh oh!"); }
// Note: passing the function by name, *not* calling it. let withFallback = tryOr<number, string>("fallback"); let theResult = await withFallback(throws); if (theResult.isErr) { console.error(theResult.error); // "fallback" } ```
Note that in the curried form, you must specify the expected
Ttype of the resultingTask, or else it will always beunknown.Parameter rejection
The value to use if the
Promiserejects.Parameter fn
A function which returns a
Promisewhen called.Returns
A
Taskwhich resolves to the resolution value of the promise or rejects with the rejection value of the promise *or* any error thrown while invokingfn.
function tryOrElse
tryOrElse: { <T, E>(onError: (reason: unknown) => E, fn: () => PromiseLike<T>): Task<T, E>; <T, E>(onError: (reason: unknown) => E): ( fn: () => PromiseLike<T> ) => Task<T, E>;};Given a function which takes no arguments and returns a
PromiseLikeand a function which accepts anunknownrejection reason and transforms it into a known rejection typeE, return a for the result of invoking that function. This safely handles functions which fail synchronously or asynchronously, so unlike is safe to use with values which may throw errors _before_ producing aPromise.## Examples
```ts import { tryOrElse } from 'true-myth/task';
function throws(): Promise { throw new Error("Uh oh!"); }
// Note: passing the function by name, *not* calling it. let theTask = tryOrElse( (reason) =>
Something went wrong: ${reason}, throws ); let theResult = await theTask; console.log(theResult.toString); // Err("Something went wrong: Error: Uh oh!") ```You can also write this in “curried” form, passing just the fallback value and getting back a function which accepts the:
```ts import { tryOrElse } from 'true-myth/task';
function throws(): Promise { throw new Error("Uh oh!"); }
// Note: passing the function by name, *not* calling it. let withFallback = tryOrElse<number, string>( (reason) =>
Something went wrong: ${reason}); let theResult = await withFallback(throws); console.log(theResult.toString); // Err("Something went wrong: Error: Uh oh!") ```Note that in the curried form, you must specify the expected
Ttype of the resultingTask, or else it will always beunknown.Parameter onError
The function to use to transform the rejection reasons if the
PromiseLikeproduced byfnrejects.Parameter fn
A function which returns a
PromiseLikewhen called.Returns
A
Taskwhich resolves to the resolution value of the promise or rejects with the rejection value of the promise *or* any error thrown while invokingfn.
function withResolvers
withResolvers: <T, E>() => WithResolvers<T, E>;Standalone function version of
function withRetries
withRetries: <T, E>( retryable: (status: RetryStatus) => Task<T, E | StopRetrying> | StopRetrying, strategy?: delay.Strategy) => Task<T, RetryFailed<E>>;Execute a callback that produces either a or the “sentinel” [
Error][error-mdn] subclass .withRetriesretries theretryablecallback until the retry strategy is exhausted *or* until the callback returns eitherStopRetryingor aTaskthat rejects withStopRetrying. If no strategy is supplied, a default strategy of retrying immediately up to three times is used.[error-mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
The
strategyis any iterable iterator that produces an integral number, which is used as the number of milliseconds to delay before retrying theretryable. When thestrategystops yielding values, this will produce aTaskwhose rejection value is an instance of .Returning
stopRetrying()from the top-level of the function or as the rejection reason will also produce a rejectedTaskwhose rejection value is an instance ofRetryFailed, but will also immediately stop all further retries and will include theStopRetryinginstance as thecauseof theRetryFailedinstance.You can determine whether retries stopped because the strategy was exhausted or because
stopRetryingwas called by checking thecauseon theRetryFailedinstance. It will beundefinedif the theRetryFailedwas the result of the strategy being exhausted. It will be aStopRetryingif it stopped because the caller returnedstopRetrying().## Examples
### Retrying with backoff
When attempting to fetch data from a server, you might want to retry if and only if the response was an HTTP 408 response, indicating that there was a timeout but that the client is allowed to try again. For other error codes, it will simply reject immediately.
```ts import * as task from 'true-myth/task'; import * as delay from 'true-myth/task/delay';
let theTask = task.withRetries( () => task.fromPromise(fetch('https://example.com')).andThen((res) => { if (res.status === 200) { return task.fromPromise(res.json()); } else if (res.status === 408) { return task.reject(res.statusText); } else { return task.stopRetrying(res.statusText); } }), delay.fibonacci().map(delay.jitter).take(10) ); ```
Here, this uses a Fibonacci backoff strategy, which can be preferable in some cases to a classic exponential backoff strategy (see [A Performance Comparison of Different Backoff Algorithms under Different Rebroadcast Probabilities for MANET's][pdf] for more details).
[pdf]: https://www.researchgate.net/publication/255672213_A_Performance_Comparison_of_Different_Backoff_Algorithms_under_Different_Rebroadcast_Probabilities_for_MANET's
### Manually canceling retries
Sometimes, you may determine that the result of an operation is fatal, so there is no point in retrying even if the retry strategy still allows it. In that case, you can return the special
StopRetryingerror produced by callingstopRetryingto immediately stop all further retries.For example, imagine you have a library function that returns a custom
Errorsubclass that includes anisFatalvalue on it, something like this::```ts class AppError extends Error { isFatal: boolean; constructor(message: string, options?: { isFatal?: boolean, cause?: unknown }) { super(message, { cause: options?.cause }); this.isFatal = options?.isFatal ?? false; } } ```
You could check that flag in a
Taskrejection and returnstopRetrying()if it is set:```ts import * as task from 'true-myth/task'; import { fibonacci, jitter } from 'true-myth/task/delay'; import { doSomethingThatMayFailWithAppError } from 'someplace/in/my-app';
let theTask = task.withRetries( () => { doSomethingThatMayFailWithAppError().orElse((rejection) => { if (rejection.isFatal) { return task.stopRetrying("It was fatal!", { cause: rejection }); }
return task.reject(rejection); }); }, fibonacci().map(jitter).take(20) ); ```
### Using the retry
statusparameterEvery time
withRetriestries theretryable, it provides the current count of attempts and the total elapsed duration as properties on thestatusobject, so you can do different things for a given way of trying the async operation represented by theTaskdepending on the count. Here, for example, the task is retried if the HTTP request rejects, with an exponential backoff starting at 100 milliseconds, and captures the number of retries in anErrorwrapping the rejection reason when the response rejects or when converting the response to JSON fails. It also stops if it has tried the call more than 10 times or if the total elapsed time exceeds 10 seconds.```ts import * as task from 'true-myth/task'; import { exponential, jitter } from 'true-myth/task/delay';
let theResult = await task.withRetries( ({ count, elapsed }) => { if (count > 10) { return task.stopRetrying(
Tried too many times: ${count}); }if (elapsed > 10_000) { return task.stopRetrying(
Took too long: ${elapsed}ms); }return task.fromPromise(fetch('https://www.example.com/')) .andThen((res) => task.fromPromise(res.json())) .orElse((cause) => { let message =
Attempt #${count} failed; return task.reject(new Error(message, { cause })); }); }, exponential().map(jitter), ); ```### Custom strategies
While the module supplies a number of useful strategies, you can also supply your own. The easiest way is to write [a generator function][gen], but you can also implement a custom iterable iterator, including by providing a subclass of the ES2025
Iteratorclass.Here is an example of using a generator function to produce a random but [monotonically increasing][monotonic] value proportional to the current value:
```ts import * as task from 'true-myth/task';
function* randomIncrease(options?: { from: number }) { // always use integral values, and default to one second. let value = options ? Math.round(options.from) : 1_000; while (true) { yield value; value += Math.ceil(Math.random() * value); // always increase! } }
await task.withRetries(({ count }) => { let delay = Math.round(Math.random() * 100); return task.timer(delay).andThen((time) => task.reject(
Rejection #${count} after ${time}ms), ); }, randomIncrease(10).take(10)); ```[monotonic]: https://en.wikipedia.org/wiki/Monotonic_function
Parameter retryable
A callback that produces a .
Parameter strategy
An iterable iterator that produces an integral number of milliseconds to wait before trying
retryableagain. If not supplied, theretryablewill be retried immediately up to three times.T The type of a . E The type of a .
class AggregateRejection
class AggregateRejection<E extends unknown[]> extends Error {}An error type produced when produces any rejections. All rejections are aggregated into this type.
> [!NOTE] > This error type is not allowed to be subclassed.
E The type of the rejection reasons.
constructor
constructor(errors: unknown[]);property errors
readonly errors: unknown[];property name
readonly name: string;method toString
toString: () => string;class InvalidAccess
class InvalidAccess extends Error {}constructor
constructor(field: 'value' | 'reason', state: State);property name
readonly name: string;class RetryFailed
class RetryFailed<E> extends Error {}An [
Error][mdn-error] subclass for when aTaskrejected after a specified number of retries. It includes all rejection reasons, including the final one, as well as the number of retries and the total duration spent on the retries. It distinguishes between the list of rejections and the optionalcauseproperty inherited fromErrorso that it can indicate if the retries failed because the retry strategy was exhausted (in which casecausewill beundefined) or because the caller returned a instance (in which casecausewill be that instance.)You can neither construct nor subclass this error, only use its properties. If you need to check whether an
Errorclass is an instance of this class, you can check whether itsnameis or you can use the helper function:```ts import * as task from 'true-myth/task';
// snip let result = await someFnThatReturnsATask(); if (result.isErr) { if (task.isRetryFailed(result.error)) { if (result.error.cause) { console.error('You quit on purpose: ', cause); }
for (let rejection of result.error.rejections) { console.error(rejection); } } else { // handle other error types } } ```
[mdn-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
Errors
property name
readonly name: string;property rejections
readonly rejections: E[];Set of all rejections collected during retries.
property totalDuration
readonly totalDuration: number;Elapsed time in milliseconds.
property tries
readonly tries: number;Number of retries before the task failed.
class StopRetrying
class StopRetrying extends Error {}A custom [
Error][mdn-error] subclass which acts as a “sentinel”: when you return it either as the top-level return value from the callback for or the rejection reason for a produces bywithRetries, the function will stop retrying immediately.[mdn-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
You can neither construct this class directly nor subclass it. Instead, use the helper function to construct it.
Errors
property name
readonly name: string;class TaskExecutorException
class TaskExecutorException extends Error {}The error thrown when an error is thrown in the executor passed to . This error class exists so it is clear exactly what went wrong in that case.
Errors
constructor
constructor(originalError: {});property name
name: string;class Timeout
class Timeout extends Error {}An
Errortype representing a timeout, as when a elapses.
constructor
constructor(ms: number);property duration
readonly duration: number;property ms
readonly ms: number;class UnsafePromise
class UnsafePromise extends Error {}An error thrown when the
Promise<Result<T, E>>passed to fromUnsafePromise rejects.Errors
constructor
constructor(unhandledError: {});property name
readonly name: string;interface Pending
interface Pending<T, E> extends Omit<TaskImpl<T, E>, 'value' | 'reason'> {}A that has not yet resolved.
T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Task Variants
index signature
get isPending(): true;index signature
get isResolved(): false;index signature
get isRejected(): false;index signature
get state(): typeof State.Pending;interface Rejected
interface Rejected<T, E> extends Omit<TaskImpl<T, E>, 'value'> {}A that has rejected. Its
reasonis of typeE.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Task Variants
index signature
get isPending(): false;index signature
get isResolved(): false;index signature
get isRejected(): true;index signature
get state(): typeof State.Rejected;index signature
get reason(): E;interface Resolved
interface Resolved<T, E> extends Omit<TaskImpl<T, E>, 'reason'> {}A that has resolved. Its
valueis of typeT.T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.Task Variants
index signature
get isPending(): false;index signature
get isResolved(): true;index signature
get isRejected(): false;index signature
get state(): typeof State.Resolved;index signature
get value(): T;interface RetryStatus
interface RetryStatus {}Information about the current retryable call status.
interface TaskConstructor
interface TaskConstructor {}The public interface for the class *as a value*: a constructor and the associated static properties.
method reject
reject: { <T = never, E extends {} = {}>(): Task<T, Unit>; <T = never, E = unknown>(reason: E): Task<T, E>;};Construct a
Taskwhich is already rejected. Useful when you have an error already, but need it to be available in an API which expects aTask.Constructors
method resolve
resolve: { <T extends Unit, E = never>(): Task<Unit, E>; <T, E = never>(value: T): Task<T, E>;};Construct a
Taskwhich is already resolved. Useful when you have a value already, but need it to be available in an API which expects aTask.Constructors
method withResolvers
withResolvers: <T, E>() => WithResolvers<T, E>;Create a pending
Taskand supplyresolveWithandrejectWithhelpers, similar to the [Promise.withResolvers][pwr] static method, but producing aTaskwith the usual safety guarantees.[pwr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
## Examples
### Resolution
```ts let { task, resolveWith, rejectWith } = Task.withResolvers<string, Error>(); resolveWith("Hello!");
let result = await task.map((s) => s.length); let length = result.unwrapOr(0); console.log(length); // 5 ```
### Rejection
```ts let { task, resolveWith, rejectWith } = Task.withResolvers<string, Error>(); rejectWith(new Error("oh teh noes!"));
let result = await task.mapRejection((s) => s.length); let errLength = result.isErr ? result.error : 0; console.log(errLength); // 5 ```
Constructors
construct signature
new <T, E>( executor: (resolve: (value: T) => void, reject: (reason: E) => void) => void): Task<T, E>;Construct a new
Task, using callbacks to wrap APIs which do not natively provide aPromise.This is identical to the [Promise][promise] constructor, with one very important difference: rather than producing a value upon resolution and throwing an exception when a rejection occurs like
Promise, aTaskalways “succeeds” in producing a usable value, just like for synchronous code.[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
For constructing a
Taskfrom an existingPromise, see:- - - -
For constructing a
Taskimmediately resolved or rejected with given values, see and respectively.Parameter executor
A function which the constructor will execute to manage the lifecycle of the
Task. The executor in turn has two functions as parameters: one to call on resolution, the other on rejection.
type Matcher
type Matcher<T, E, A> = { Resolved: (value: T) => A; Rejected: (reason: E) => A;};A lightweight object defining how to handle each outcome state of a .
type Task
type Task<T, E> = Pending<T, E> | Resolved<T, E> | Rejected<T, E>;A
Taskis a type safe asynchronous computation.You can think of a
Task<T, E>as being basically aPromise<Result<T, E>>, because it *is* aPromise<Result<T, E>>under the hood, but with two main differences from a “normal”Promise:1. A
Task*cannot* “reject”. All errors must be handled. This means that, like a , it will *never* throw an error if used in strict TypeScript.2. Unlike
Promise,Taskrobustly distinguishes betweenmapandandThenoperations.Taskalso implements JavaScript’sPromiseLikeinterface, so you canawaitit; when aTask<T, E>is awaited, it produces a .T The type of the value when the
Taskresolves successfully. E The type of the rejection reason when theTaskrejects.
type TaskTypesFor
type TaskTypesFor<A extends readonly AnyTask[]> = { resolution: { -readonly [P in keyof A]: ResolvesTo<A[P]>; }; rejection: { -readonly [P in keyof A]: RejectsWith<A[P]>; };};type Timer
type Timer = Task<number, never>;A specialized for use with or other methods or functions which want to know they are using.
> [!NOTE] > This type has zero runtime overhead, including for construction: it is just > a
Taskwith additional *type information*.
type WithResolvers
type WithResolvers<T, E> = { task: Task<T, E>; resolve: (value: T) => void; reject: (reason: E) => void;};Type returned by calling
namespace delay
module 'dist/task/delay.d.ts' {}Types and helpers for managing retry delays with the
withRetrieshelper fromTask.
function exponential
exponential: (options?: { from?: number; withFactor?: number;}) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing exponentially until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.By default, this increases exponentially by a factor of 2; you may optionally pass
{ factor: someOtherValue }to change the exponentiation factor.If you pass a non-integral value as
base, it will be rounded to the nearest integral value usingMath.round.
function fibonacci
fibonacci: (options?: { from: number }) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing as a Fibonacci sequence (1, 1, 2, 3, 5, 8, 13, ...) until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.If you pass a non-integral value as the
fromproperty on the configuration argument, it will be rounded to the nearest integral value usingMath.round.
function fixed
fixed: (options?: { at: number }) => Generator<number>;Generate an infinite iterable of the same integer value in milliseconds.
If you pass a non-integral value, like
{ at: 2.5 }, it will be rounded to the nearest integral value usingMath.round, i.e.3in that case.
function immediate
immediate: () => Generator<number>;Generate an infinite iterable of the value
0.
function jitter
jitter: (n: number) => number;Apply fully random jitter proportional to the number passed in. The resulting value will never be larger than 2×n, and never less than 0.
This is useful for making sure your retries generally follow a given , but if multiple tasks start at the same time, they do not all retry at exactly the same time.
Parameter n
The value to apply random jitter to.
function linear
linear: (options?: { from?: number; withStepSize?: number;}) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing linearly (1, 2, 3, 4, 5, 5, 7, ...) until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.By default, this increases by a step size of 1; you may optionally pass
{ step: someOtherValue }to change the step size.If you pass a non-integral value as
base, it will be rounded to the nearest integral value usingMath.round.
function none
none: () => Generator<number>;A “no-op” strategy, for if you need to call supply a to a function but do not actually want to retry at all.
You should never use this directly with
Task.withRetries; in the case where you would, invoke theTaskthat would be retried directly (i.e. without usingwithRetriesat all) instead.
interface Strategy
interface Strategy extends Iterator<number> {}A retry delay strategy is just an
Iterator<number>.For details on how to use or implement a
Strategy, as well as why it exists as a distinct type, see [the guide][guide].[guide]: /guide/understanding/task/retries-and-delays
namespace Delay
module 'dist/task/delay.d.ts' {}Types and helpers for managing retry delays with the
withRetrieshelper fromTask.
function exponential
exponential: (options?: { from?: number; withFactor?: number;}) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing exponentially until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.By default, this increases exponentially by a factor of 2; you may optionally pass
{ factor: someOtherValue }to change the exponentiation factor.If you pass a non-integral value as
base, it will be rounded to the nearest integral value usingMath.round.
function fibonacci
fibonacci: (options?: { from: number }) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing as a Fibonacci sequence (1, 1, 2, 3, 5, 8, 13, ...) until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.If you pass a non-integral value as the
fromproperty on the configuration argument, it will be rounded to the nearest integral value usingMath.round.
function fixed
fixed: (options?: { at: number }) => Generator<number>;Generate an infinite iterable of the same integer value in milliseconds.
If you pass a non-integral value, like
{ at: 2.5 }, it will be rounded to the nearest integral value usingMath.round, i.e.3in that case.
function immediate
immediate: () => Generator<number>;Generate an infinite iterable of the value
0.
function jitter
jitter: (n: number) => number;Apply fully random jitter proportional to the number passed in. The resulting value will never be larger than 2×n, and never less than 0.
This is useful for making sure your retries generally follow a given , but if multiple tasks start at the same time, they do not all retry at exactly the same time.
Parameter n
The value to apply random jitter to.
function linear
linear: (options?: { from?: number; withStepSize?: number;}) => Generator<number>;Generate an infinite iterable of integers beginning with
baseand increasing linearly (1, 2, 3, 4, 5, 5, 7, ...) until reachingNumber.MAX_SAFE_INTEGER, after which the generator will continue yieldingNumber.MAX_SAFE_INTEGERforever.By default, this increases by a step size of 1; you may optionally pass
{ step: someOtherValue }to change the step size.If you pass a non-integral value as
base, it will be rounded to the nearest integral value usingMath.round.
function none
none: () => Generator<number>;A “no-op” strategy, for if you need to call supply a to a function but do not actually want to retry at all.
You should never use this directly with
Task.withRetries; in the case where you would, invoke theTaskthat would be retried directly (i.e. without usingwithRetriesat all) instead.
interface Strategy
interface Strategy extends Iterator<number> {}A retry delay strategy is just an
Iterator<number>.For details on how to use or implement a
Strategy, as well as why it exists as a distinct type, see [the guide][guide].[guide]: /guide/understanding/task/retries-and-delays
namespace toolbelt
module 'dist/toolbelt.d.ts' {}Tools for working easily with
MaybeandResult*together*... but which do not *require* you to use both. If they were in thetrue-myth/maybeortrue-myth/resultmodules, then importing either would always include the other. While that is not usually a concern with bundlers, it *is* an issue when using dynamic imports or otherwise doing runtime resolution in a browser or similar environment.The flip side of that is: importing from *this* module *does* require access to both
MaybeandResultmodules.
function fromMaybe
fromMaybe: { <T extends {}, E>(errValue: E, maybe: Maybe<T>): Result<T, E>; <T extends {}, E>(errValue: E): (maybe: Maybe<T>) => Result<T, E>;};Transform a into a .
If the
Maybeis a , its value will be wrapped in the variant; if it is a , theerrValuewill be wrapped in the variant.Parameter errValue
A value to wrap in an
Errifmaybeis aNothing.Parameter maybe
The
Maybeto convert to aResult.
function fromResult
fromResult: <T extends {}>(result: Result<T, unknown>) => Maybe<T>;Construct a from a .
If the
Resultis a , wrap its value in . If theResultis an , throw away the wrappedEand transform to a .T The type of the value wrapped in a and therefore in the of the resulting
Maybe.Parameter result
The
Resultto construct aMaybefrom.Returns
JustifresultwasOkorNothingif it wasErr.
function toMaybe
toMaybe: <T extends {}>(result: Result<T, unknown>) => Maybe<T>;Convert a to a .
The converted type will be if the
Resultis or if theResultis ; the wrapped error value will be discarded.Parameter result
The
Resultto convert to aMaybeReturns
Justthe value inresultif it isOk; otherwiseNothing
function toOkOrElseErr
toOkOrElseErr: { <T extends {}, E>(elseFn: () => E, maybe: Maybe<T>): Result<T, E>; <T extends {}, E>(elseFn: () => E): (maybe: Maybe<T>) => Result<T, E>;};Transform the into a , using the wrapped value as the value if the
Maybeis ; otherwise usingelseFnto generate the .T The wrapped value. E The error type to in the
Result.Parameter elseFn
The function which generates an error of type
E.Parameter maybe
The
Maybeinstance to convert.Returns
A
Resultcontaining the value wrapped inmaybein anOk, or the value generated byelseFnin anErr.
function toOkOrErr
toOkOrErr: { <T extends {}, E>(error: E, maybe: Maybe<T>): Result<T, E>; <T extends {}, E>(error: E): (maybe: Maybe<T>) => Result<T, E>;};Transform the into a , using the wrapped value as the value if the
Maybeis ; otherwise using the suppliederrorvalue for .T The wrapped value. E The error type to in the
Result.Parameter error
The error value to use if the
MaybeisNothing.Parameter maybe
The
Maybeinstance to convert.Returns
A
Resultcontaining the value wrapped inmaybein anOk, orerrorin anErr.
function transposeMaybe
transposeMaybe: <T extends {}, E>( maybe: Maybe<Result<T, E>>) => Result<Maybe<T>, E>;Transposes a of a into a
Resultof aMaybe.| Input | Output | | -------------- | ------------- | |
Just(Ok(T))|Ok(Just(T))| |Just(Err(E))|Err(E)| |Nothing|Ok(Nothing)|Parameter maybe
a
Maybe<Result<T, E>>to transform to aResult<Maybe<T>, E>>.
function transposeResult
transposeResult: <T extends {}, E>( result: Result<Maybe<T>, E>) => Maybe<Result<T, E>>;Transposes a of a into a
Maybeof aResult.| Input | Output | | ------------- | -------------- | |
Ok(Just(T))|Just(Ok(T))| |Err(E)|Just(Err(E))| |Ok(Nothing)|Nothing|Parameter result
a
Result<Maybe<T>, E>to transform to aMaybe<Result<T, E>>.
Package Files (8)
Dependencies (0)
No dependencies.
Dev Dependencies (24)
Peer Dependencies (0)
No peer dependencies.
Badge
To add a badge like this oneto your package's README, use the codes available below.
You may also use Shields.io to create a custom badge linking to https://www.jsdocs.io/package/true-myth.
- Markdown[](https://www.jsdocs.io/package/true-myth)
- HTML<a href="https://www.jsdocs.io/package/true-myth"><img src="https://img.shields.io/badge/jsDocs.io-reference-blue" alt="jsDocs.io"></a>
- Updated .
Package analyzed in 10844 ms. - Missing or incorrect documentation? Open an issue for this package.
