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
: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';
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:
- It now requires TypeScript 4.7+ and Node 14+. This allows us to use Node's
exports
syntax without hacks.
- 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:
Update your project to at least Node 14 and TypeScript 4.7.
Switch from using any deprecated code to the supported replacements (as described in the docs for each deprecated function).
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.
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
: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>;
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 enum
s. 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';
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';
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);
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);
let aJustOk: Maybe<Result<number, string>> = just(ok(12));
let result: Maybe<Result<number, string>> = Result.transposeMaybe(aJustOk);
console.log(result);
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
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');
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);
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