Détail du package

true-myth

true-myth1.2mMIT9.0.0

A library for safe functional programming in JavaScript, with first-class support for TypeScript

typescript, functional programming, maybe, result

readme

True Myth

True Myth provides safe, idiomatic null, error, and async code handling in TypeScript, with Maybe, Result, and Task types that are really nice.

Test coverage: 100% npm supported Node versions supported TypeScript versions Nightly TypeScript Run Stability: Active DNS by JS.org docs built with Typedoc

READMEAPI docsSourceIntro blog post

Overview

True Myth provides standard, type-safe wrappers and helper functions to help you with three extremely common cases in programming:

  • not having a value
  • having a result where you need to deal with either success or failure
  • having an asynchronous operation which may fail

You could implement all of these yourself – it's not hard! – but it's much easier to just have one extremely well-tested library you can use everywhere to solve this problem once and for all.

See the docs for setup, guides, and API docs!

Contents

Requirements

  • Node 20+
  • TS 5.3+
  • tsconfig.json:
    • moduleResolution: use "Node16" or later
    • strict: true
  • package.json
    • type: "module" (or else use import() to import True Myth into a commonJS build)

For details on using a pure ES modules package in TypeScript, see the TypeScript handbook's guide.

Compatibility

This project follows the current draft of the Semantic Versioning for TypeScript Types specification.

  • Currently supported TypeScript versions: 5.3, 5.4, 5.5, 5.6, and 5.7
  • Compiler support policy: simple majors
  • Public API: all published types not in a -private module are public

Basic bundle size info

Size of the ESM build without tree-shaking (yes, these are in bytes: this is a pretty small library!):

file size (B) terser[^terser] (B) terser and brotli[^brotli] (B)
-private/utils.js 888 321 166
index.js 646 273 108
maybe.js 18683 3553 889
result.js 14111 3258 812
task/delay.js 3901 649 259
task.js 51219 7254 1997
test-support.js 473 142 89
toolbelt.js 3739 890 277
unit.js 656 58 57
total[^total] 94316 16398 4654

Notes:

  • The unmodified size includes comments.
  • Thus, running through Terser gets us a much more realistic size: about 16.4KB to parse.
  • The total size across the wire of the whole library will be ~4.7KB.
  • This is all tree-shakeable to a significant degree. If your production bundle does not import or use anything from true-myth/test-support, you will not pay for it. However, some parts of the library do depend directly on other parts: for example, toolbelt uses exports from result and maybe, and Task makes extensive use of Result under the hood.

[^terser]: Using terser 5.37.0 with --compress --mangle --mangle-props.

[^brotli]: Generated by running gzip -kq11 on the result of the terser invocation.

[^total]: This is just the sum of the previous lines. Real-world bundle size is a function of what you actually use, how your bundler handles tree-shaking, and how the results of bundling compresses. Notice that sufficiently small files can end up larger after compression; this stops being an issue once part of a bundle.

Inspiration

The design of True Myth draws heavily on prior art; essentially nothing of this is original – perhaps excepting the choice to make Maybe.of handle null and undefined in constructing the types. In particular, however, True Myth draws particular inspiration from:

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

9.0.0 (2025-04-16)

Now with nicer internals. cleaner public APIs, a brand new docs site! For more details, see the announcement blog post.

Changes

:boom: Breaking Change

:rocket: Enhancement

:memo: Documentation

:house: Internal

:wastebasket: Deprecation

Committers: 2

8.6.0 (2025-04-15)

The headlining feature here is making the andThen and orElse functions work more like people intuitively expect them to! See #1003 for details. Additionally, this fixes a long-standing (but easy-to-miss) bug in the behavior of maybe.get.

This final release in the v8.x series includes all the features which will be present on v9.0. To prepare for the v9.0 release, all you need to do is make sure you are on a sufficiently recent version of TypeScript and switch from any deprecated functions to their supported replacements.

Changes

:rocket: Enhancement

  • #1003 Feature: generalize inference for andThen and orElse [backport] (@chriskrycho)

:bug: Bug Fix

Committers: 1

8.5.3 (2025-04-09)

Changes

:bug: Bug Fix

Committers: 1

8.5.2 (2025-04-07)

Changes

This mostly comes down to fixing a bug introduced in v8.5.1 in the definition of Strategy, but it brings along some documentation benefits as well.

(:bug: Bug Fix)

(:memo: Documentation)

Committers: 1

8.5.1 (2025-04-02)

The first of what will likely be several 8.5.x bug fix releases to address issues found in the past few months!

(:bug: Bug Fix)

Committers: 1

8.5.0 (2025-01-25)

Adds a powerful Task.withRetries utility for retrying tasks, along with a new module, true-myth/task/delay, which provides a bunch of useful strategies for retries. See the docs for more details, and thanks to @alfierivera for suggesting it!

(:rocket: Enhancement)

(:house: Internal)

Committers: 1

8.4.0 (2025-01-11)

Highlight: adds auto-curried, module-scope versions of all the Task instance methods like map and andThen and more. Hot on the heels of 8.3.0, with what we think is probably the last set of features for this release!

(:rocket: Enhancement)

(:memo: Documentation)

(:house: Internal)

Committers: 1

8.3.0 (2025-01-10)

Highlights:

  • All the missing Task combinators that weren’t part of v8.3.0: all, allSettled, any, race, as well as fancy new timer and timeout functions.
  • New safe functions in the Result and Task modules, for transforming a throwing function into one you can call safely and get a Result or Task instead.
  • Module-scope versions of all the major Task constructors.
  • A new name for the wrapReturn function in the Maybe module, safe, to match the same function in the Result and Task modules

This release also introduces some deprecations to tackle some mistakes we (read: @chriskrycho) made in the initial release of Task in v8.2.0.

[!NOTE] We will be releasing v9.0.0 very soon, removing those deprecations and updating our TypeScript support matrix. However, we expect 8.3 to be stable enough that you could stay on it without issues—potentially for years. If there are any showstopper bugs, we will of course backport a fix for them, but there shouldn’t be any!

(:rocket: Enhancement)

(:memo: Documentation)

(:house: Internal)

(:wastebasket: Deprecation)

  • #909 Maybe: rename wrapReturn to safe (@chriskrycho)
  • #908 Task: deprecate Task.try, Task.tryOr, and Task.tryOrElse (@chriskrycho)
  • #913 Task: deprecate static fromResult, fromPromise, and fromUnsafePromise

Committers: 2

8.2.0 (2025-01-03)

Finally—finally!—True Myth get a Task type! A Task<T, E> is like a Promise<Result<T, E>>. In fact, under the hood, it is exactly a Promise<Result<T, E>>, but in general you do not need to think about that layering. Instead, you get a nice type-safe API for fallible async operations. (It’s what Promise should have been!)

:rocket: Enhancement

:memo: Documentation

  • #890 docs: improve rendering of the fancy variant shenanigans with @class (@chriskrycho)

:house: Internal

Committers: 1

8.2.0-beta.1 (2024-12-31)

Beta release with Task, so folks can easily test it out!

:bug: Bug Fix

  • #887 Result: correct the implementation of static err constructor (@chriskrycho)

:memo: Documentation

:house: Internal

Committers: 1

8.1.0 (2024-12-04)

The big feature: a new module just for test support, with two functions in it: unwrap and unwrapErr. You can now write this:

import { expect, test } from 'vitest'; // or your testing library of choice

import Maybe from 'true-myth/maybe';
import Result from 'true-myth/result';
import { unwrap, unwrapErr } from 'true-myth/test-helpers';

import { producesMaybe, producesResult } from 'my-library';

test('using this new helper', () => {
  expect(unwrap(producesMaybe())).toBe(true);
  expect(unwrap(producesResult('valid'))).toBe('cool');
  expect(unwrapErr(producesResult('invalid')).toBe('oh teh noes');
});

See the docs for more!

:rocket: Enhancement

:memo: Documentation

:house: Internal

Committers: 2

8.0.1 (2024-08-22)

:bug: Bug Fix

  • #811 fix: do not set sourceRoot in tsconfigs (@chriskrycho) – this should make source maps resolve correctly!

Committers: 1

8.0.0 (2024-08-11)

This is a pretty small “breaking” release: it makes a change so True Myth is more type safe than it was before, specifically when constructing known-Ok or known-Err types with Result.ok and Result.err. In earlier versions, if you wrote Result.ok(123), the type would be Result<number, unknown>. New contributor @auvred pointed out that in that case, we know there is never an error type, though, so we can use the never type. This is breaking in that you may have to explicitly annotate some types where you did not before, because of the assignability rules for unknown and never (cf. this playground).

Net, very little of your code should have to change, but where it does, it will be safer than it was before! Thanks to @auvred for the improvement!

:boom: Breaking Change

  • #789 fix: set error type in Result.ok and ok type in Result.err to never by default (@auvred)

:rocket: Enhancement

  • #789 fix: set error type in Result.ok and ok type in Result.err to never by default (@auvred)

:memo: Documentation

Committers: 2

7.4.0 (2024-07-10)

:rocket: Enhancement

:memo: Documentation

Committers: 2

7.3.0 (2024-05-26)

:rocket: Enhancement

Committers: 1

7.2.1 (2024-05-26)

:bug: Bug Fix

  • #755 Correctly handle null and undefined in function return types (@chriskrycho)

:house: Internal

Committers: 1

7.2.0 (2024-05-16)

:rocket: Enhancement

:house: Internal

Committers: 2

7.1.0 (2023-09-05)

:rocket: Enhancement

:memo: Documentation

:house: Internal

Committers: 2

7.0.1 (2023-07-16)

:bug: Bug Fix

:house: Internal

Committers: 1

7.0.0 (2023-07-15)

:boom: Breaking Change

  • #562 [Breaking] Require Node 18, adopt pnpm, use latest release-it (@chriskrycho)

:rocket: Enhancement

:memo: Documentation

:house: Internal

Committers: 3

6.2.0 (2022-09-08)

:rocket: Enhancement

:bug: Bug Fix

:memo: Documentation

:house: Internal

Committers: 2

6.1.0 (2022-07-08)

:rocket: Enhancement

  • Re-export Toolbelt from root for consumers not using TS 4.7’s exports support (@chriskrycho)

:house: Internal

Committers: 1

6.0.0 (2022-05-25)

There are two significant breaking changes in v6.0:

  1. It now requires TypeScript 4.7+ and Node 14+. This allows us to use Node's exports syntax without hacks.
  2. It removes items deprecated in the 4.x and 5.x cycles. This allows us to provide better tree-shaking. Previously, using either the Result or Maybe classes meant you also pulled in the other, since they had code to interoperate with each other. These now live only in the toolbelt module, along with the Array helpers.

To upgrade:

  1. Update your project to at least Node 14 and TypeScript 4.7.

  2. Switch from using any deprecated code to the supported replacements (as described in the docs for each deprecated function).

  3. Set compilerOptions.module to Node16 or nodenext in your tsconfig.json. Note: this is the most significant breaking change here: it requires that every other TS package you consume also be compatible with the new mode, and if you are using True Myth in a library, cascades that requirement to your consumers as well.

  4. Update to True Myth v6. :tada:

:boom: Breaking Change

:rocket: Enhancement

:bug: Bug Fix

:memo: Documentation

:house: Internal

Committers: 2

5.4.0 (2022-05-25)

This is the final release for v5.x, and only exists to make sure there are fully overlapping supported TypeScript versions for True Myth v5 and v6. See the release notes for the upcoming v6.0 release for upgrade notes.

:rocket: Enhancement

:house: Internal

Committers: 1

5.3.1 (2022-04-22)

:bug: Bug Fix

Committers: 1

5.3.0 (2022-04-22)

:rocket: Enhancement

:bug: Bug Fix

Committers: 2

5.2.0 (2022-03-07)

:rocket: Enhancement

Committers: 1

5.1.3 (2022-03-06)

:bug: Bug Fix

Committers: 1

5.1.2 (2021-12-27)

:bug: Bug Fix

:memo: Documentation

Committers: 1

5.1.1 (2021-12-16)

:rocket: Enhancement

:bug: Bug Fix

:house: Internal

Committers: 1

5.1.0 (2021-12-12)

:rocket: Enhancement

Committers: 1

5.0.1 (2021-12-11)

:bug: Bug Fix

:memo: Documentation

Committers: 1

5.0.0

:boom: Changed

  • The top-level namespace-style export has been removed. If you were relying on the static members to be present when doing import Maybe from 'true-myth/maybe' or import Result from 'true-myth/result';, you can replace them with import * as Maybe from 'true-myth/maybe'; or import * as Result from 'true-myth/result';. This should make for much better tree-shaking with bundlers like Rollup, which can see “through” a namespace import in a way they cannot with a manually-created namespace object. Where you want to maintain the type and namespace imports, you can do this:

      import * as Maybe from 'true-myth/maybe';
      type Maybe<T> = Maybe.Maybe<T>; // convenience alias

    In general, though, you should prefer to simply import the named functions instead:

      import Maybe, { transposeArray } from 'true-myth/maybe';
  • There are no longer separate classes for Just, Nothing, Ok, and Err. This substantially reduces the size of the library, and should hopefully improve browsers' ability to optimize code using these, since there is now only one class in each case: Maybe and Result. The public API for the classes is unchanged, but if you relied on instanceof checks against those classes anywhere, those checks will no longer work.

  • The exported Variant types are now frozen, constant objects, not TypeScript enums. This should not break anyone, since the only difference in observable behavior between an enum and a const is the ability to do a “reverse lookup” on an enum—but since the field names and their values are identical, this just means shipping less, and faster, code.

  • The isJust, isNothing, isOk, and isErr methods have been converted to getters. This makes them muchmore immediately useful in contexts where invoking a function is annoying, for any reason—for example, in Ember templates.

  • We no longer publish CommonJS modules, only ES Modules.

  • Dropped support for Node versions earlier than Node 12 LTS.

  • Support for versions of TypeScript before 4.0 have been removed, to enable the type-safe re-implementation of Maybe.all.

  • The MaybeShape and ResultShape interfaces are no longer exported. These were never intended for public reimplementation, and there is accordingly no value in their continuing to be public.

  • A number of aliases (originally designed to make migration from e.g. Folktale easier) have been removed:

    • cata: use match
    • chain and flatMap: use andThen
    • maybeify and transmogrify: use wrapReturn
    • unsafelyGet and unsafeGet: use .isJust/.isOk then .value
    • unsafelyGetErr and unsafelyUnwrapErr: use .isErr then .error
    • getOr: use unwrapOr
    • getOrElse: use unwrapOrElse
    • fromNullable and maybe:
      • import Maybe and use its static constructor Maybe.of
      • import the module as namespace and use Maybe.of
      • import of and alias it as you like

:star: Added

  • We introduced a new Maybe.transposeArray, which is a type-safe, renamed, merged version of Maybe.tuple and Maybe.all which can correctly handle both array and tuple types. To support this change, it now accepts arrays or tuples directly, and the variadic/spread arguments to all have been replaced with taking an array or tuple directly. While tuple and all are unchanged, they are also deprecated (see below).

    Before:

      import Maybe, { all, just, nothing, tuple } from 'true-myth/maybe';
    
      // arrays
      type ArrayResult = Maybe<Array<string | number>>;
    
      let mixedArray = [just("hello"), nothing<number>()];
      let mixedArrayResult: ArrayResult = all(...arrayOfMaybes);
    
      let allJustArray = [just("hello"), just(12)];
      let allJustArrayResult: ArrayResult = all(...allJustArray);
    
      type Tuple = [Maybe<number>, Maybe<string>];
      type TupleResult = Maybe<[number, string]>;
    
      let mixedTuple: Tuple = [just(12), just("hi")];
      let mixedTupleResult: TupleResult = tuple(mixedTuple);

    After:

      import Maybe, { arrayTranspose, just, nothing } from 'true-myth/maybe';
    
      // arrays
      type ArrayResult = Maybe<Array<string | number>>;
    
      let mixedArray = [just("hello"), nothing<number>()];
      let mixedArrayResult: ArrayResult = arrayTranspose(arrayOfMaybes);
    
      let allJustArray = [just("hello"), just(12)];
      let allJustArrayResult: ArrayResult = arrayTranspose(allJustArray);
    
      // Tuples now work with `arrayTranspose` as well.
      type Tuple = [Maybe<number>, Maybe<string>];
      type TupleResult = Maybe<[number, string]>;
    
      let mixedTuple: Tuple = [just(12), just("hi")];
      let mixedTupleResult: TupleResult = arrayTranspose(mixedTuple);
  • Maybe.transpose and Result.transpose: for when you have a Maybe<Result<T, E>> or a Result<Maybe<T>, E> and need to invert them.

    import Maybe, { just, nothing } from 'true-myth/maybe';
    import Result, { ok, err } from 'true-myth/result';
    
    let anOkJust: Result<Maybe<number>, string> = ok(just(12));
    let maybe: Maybe<number>, string> = Maybe.transposeResult(anOkJust);
    console.log(maybe); // Just(Ok(12))
    
    let aJustOk: Maybe<Result<number, string>> = just(ok(12));
    let result: Maybe<Result<number, string>> = Result.transposeMaybe(aJustOk);
    console.log(result); // Ok(Just(12))

    See the docs for further details!

    Note: these are standalone functions, not methods, because TypeScript does not support conditionally supplying a method only for one specific type parameterization.

:red_square: Deprecated

  • Maybe.tuple and Maybe.all are deprecated in favor of Maybe.arrayTranspose now correctly handles both arrays and tuples. They will be removed not earlier than 6.0.0 (timeline not decided, but not sooner than when Node 12 LTS reaches end of life on April 30, 2022).

4.1.1 (2021-01-31)

:wrench: Fixed

  • Set stripInternal to false for generated types (#97), so that they type-check.

4.1.0 (2020-12-13)

:star: Added

  • Support unwrapping to an alternative type with (backwards-compatible) tweak to type of Maybe.unwrapOr and Result.unwrapOr. For example, given let a: Maybe<string>, let b = a.unwrapOr(42) would produce a type of string | number for b. Useful particularly for interop with null and undefined at system boundaries while preserving general type safety.

🙇 Contributors

  • @alantrick (#69)
  • @C-Saunders, @flyiniggle, @bmakuh, @atrick-speedline (#63, discussion motivating #69)

4.0.0 (2019-12-18)

:wrench: Fixed

  • Switched to using namespace-style imports (import * as Maybe) internally to enable users to tree-shake.

:boom: Changed

  • Explicitly drop support for Node 8 (and specify it going forward)
  • Reverted the use of NonNullable to constraint the types of callbacks like that passed to map and mapOr, because they broke in TypeScript 3.6. (If you have ideas about how to improve this, please let us know!)

:gear: Upgrading

With yarn:

yarn upgrade true-myth@latest

With npm:

npm install true-myth@latest

🙇 Contributors

  • @chriskrycho
  • @bmakuh

3.1.0 (2019-10-08)

:star: Added

Thanks to @MarcNq, with very helpful input from @CrossEye, True Myth now has toJSON functions and methods on its types. This means that there's now a stable serialization format, which you can rely on going forward!

For Maybe<T>, the type is { variant: 'Just', value: T } or { variant: 'Nothing' }. For Result, it's { variant: 'Ok', value: T } or { variant: 'Err', error: E }. Since we just hand back the wrapped item, any object's implementation of toJSON or similar will work as usual, so you're fully in control of serialization.

:gear: Upgrading

With yarn:

yarn upgrade true-myth@latest

With npm:

npm install true-myth@latest

🙇 Contributors

  • @MarcNq
  • @CrossEye
  • @chriskrycho
  • @bmakuh

3.0.0 (2019-05-17)

:star: Added

True Myth now includes the Maybe.wrapReturn function, conveniently aliased as maybeify and Maybe.ify, which lets you take any function which includes null or undefined in its return type (like Document#querySelector and friends) and convert it to a function which returns a Maybe instead:

const querySelector = Maybe.wrapReturn(document.querySelector.bind(document));
querySelector('#neat'); // Maybe<Element>

See the docs for more!

:boom: Changed

All Maybe helper functions must now return NonNullable<T>. This example, which previously compiled and resulted in the type Maybe<string | null>, will now cause a type error:

Maybe.of(document.querySelector('#neat'))
  .map(el => el.style.color); // `color` may be `null`

SemVer note: The new behavior was the ordinary expectation for those types before—doing otherwise would cause a runtime error—and so could reasonably be described as a bugfix. Any place this type-checked before was causing a runtime error. However, it seems clearer simply to mark it as a breaking change, since it may cause your build to fail, and encourage you all to upgrade directly and fix those bugs if so!

:gear: Upgrading

With yarn:

yarn upgrade true-myth@latest

With npm:

npm install true-myth@latest

🙇 Contributors

  • @bmakuh
  • @chriskrycho
  • @snatvb