From d0ecd82230c3b010f9fc597a528c925e63403f9c Mon Sep 17 00:00:00 2001 From: Austin Weeks <168379071+austin-weeks@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:38:58 -0400 Subject: [PATCH 1/2] docs: add js docs matching readme --- src/result-async.ts | 163 ++++++++++++++++++++++++++++++++++++++++++++ src/result.ts | 59 +++++++++++++++- 2 files changed, 220 insertions(+), 2 deletions(-) diff --git a/src/result-async.ts b/src/result-async.ts index 20120d2b..e8fcd61a 100644 --- a/src/result-async.ts +++ b/src/result-async.ts @@ -19,6 +19,13 @@ import { InferOkTypes, } from './_internals/utils' +/** + * A `ResultAsync` is a Promise of a {@link Result}. + * + * It is thenable and behaves like a native `Promise`, while also + * exposing the same methods that `Result` provides without needing to `await` + * or `.then` the promise first. + */ export class ResultAsync implements PromiseLike> { private _promise: Promise> @@ -26,6 +33,15 @@ export class ResultAsync implements PromiseLike> { this._promise = res } + /** + * Same as {@link ResultAsync.fromPromise} except that it does not handle + * promise rejection. + * + * Ensure you know what you're doing: a thrown exception within this promise + * will cause `ResultAsync` to reject, instead of resolve to a `Result`. + * + * @param promise a promise that is known not to reject + */ static fromSafePromise(promise: PromiseLike): ResultAsync static fromSafePromise(promise: Promise): ResultAsync { const newPromise = promise.then((value: T) => new Ok(value)) @@ -33,6 +49,18 @@ export class ResultAsync implements PromiseLike> { return new ResultAsync(newPromise) } + /** + * Transforms a `PromiseLike` (that may reject) into a `ResultAsync`. + * + * The second argument handles the rejection case and maps the error from + * `unknown` into some type `E`. + * + * If you know the promise will not reject, use {@link ResultAsync.fromSafePromise} + * instead to avoid a redundant error handler. + * + * @param promise the promise to wrap + * @param errorFn maps a rejection reason to an error of type `E` + */ static fromPromise(promise: PromiseLike, errorFn: (e: unknown) => E): ResultAsync static fromPromise(promise: Promise, errorFn: (e: unknown) => E): ResultAsync { const newPromise = promise @@ -42,6 +70,17 @@ export class ResultAsync implements PromiseLike> { return new ResultAsync(newPromise) } + /** + * Similar to {@link Result.fromThrowable}, but for functions that return a + * `Promise`. + * + * Safer than wrapping a function call with {@link ResultAsync.fromPromise}, + * because not all promise-returning functions are `async` and may throw + * synchronously rather than returning a rejected promise. + * + * @param fn async (or promise-returning) function to wrap + * @param errorFn when an error is thrown or the promise rejects, maps it to `E` + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static fromThrowable( fn: (...args: A) => Promise, @@ -60,6 +99,17 @@ export class ResultAsync implements PromiseLike> { } } + /** + * Combines a list of `ResultAsync`s into a single `ResultAsync`. + * + * If all results are `Ok`, returns an `Ok` containing a list of all values. + * If any result is an `Err`, short-circuits and returns the first `Err`. + * + * Works on both homogeneous and heterogeneous lists (but not mixed with + * `Result`). Conceptually similar to `Promise.all`. + * + * @param asyncResultList list of `ResultAsync`s to combine + */ static combine< T extends readonly [ResultAsync, ...ResultAsync[]] >(asyncResultList: T): CombineResultAsyncs @@ -72,6 +122,15 @@ export class ResultAsync implements PromiseLike> { return (combineResultAsyncList(asyncResultList) as unknown) as CombineResultAsyncs } + /** + * Like {@link ResultAsync.combine} but without short-circuiting. + * + * Instead of returning only the first error, returns a list of all error + * values from failed results. There is no guarantee about the length of the + * error list if only some results fail. + * + * @param asyncResultList list of `ResultAsync`s to combine + */ static combineWithAllErrors< T extends readonly [ResultAsync, ...ResultAsync[]] >(asyncResultList: T): CombineResultsWithAllErrorsArrayAsync @@ -86,6 +145,15 @@ export class ResultAsync implements PromiseLike> { ) as CombineResultsWithAllErrorsArrayAsync } + /** + * Maps a `ResultAsync` to `ResultAsync` by applying a function to + * a contained `Ok` value, leaving an `Err` value untouched. + * + * The applied function can be synchronous or asynchronous (returning a + * `Promise`) with no impact to the return type. + * + * @param f The function to apply to an `Ok` value + */ map(f: (t: T) => A | Promise): ResultAsync { return new ResultAsync( this._promise.then(async (res: Result) => { @@ -98,6 +166,12 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Similar to {@link ResultAsync.andTee} except that when the passed-in + * function returns an `Err`, that error is passed along. + * + * @param f The function to apply to the current value + */ andThrough(f: (t: T) => Result | ResultAsync): ResultAsync { return new ResultAsync( this._promise.then(async (res: Result) => { @@ -114,6 +188,15 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Lets the original `ResultAsync` pass through regardless of the result + * of the passed-in function. + * + * Handy for side effects (such as logging) whose failure or success should + * not affect main logic. + * + * @param f The function to apply to the current value + */ andTee(f: (t: T) => unknown): ResultAsync { return new ResultAsync( this._promise.then(async (res: Result) => { @@ -130,6 +213,15 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Like {@link ResultAsync.andTee} for the error track. Lets the original `Err` + * value pass through regardless of the result of the passed-in function. + * + * Handy for side effects (such as logging) whose failure or success should + * not affect main logic. + * + * @param f The function to apply to the current `Err` value + */ orTee(f: (t: E) => unknown): ResultAsync { return new ResultAsync( this._promise.then(async (res: Result) => { @@ -146,6 +238,15 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Maps a `ResultAsync` to `ResultAsync` by applying a function to + * a contained `Err` value, leaving an `Ok` value untouched. + * + * The applied function can be synchronous or asynchronous (returning a + * `Promise`) with no impact to the return type. + * + * @param f a function to apply to the error `Err` value + */ mapErr(f: (e: E) => U | Promise): ResultAsync { return new ResultAsync( this._promise.then(async (res: Result) => { @@ -158,6 +259,16 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Same idea as {@link ResultAsync.map}, except the applied function must + * return a `Result` or `ResultAsync`. + * + * Always returns a `ResultAsync` regardless of the applied function's return + * type. Useful for subsequent computations that might fail, and for flattening + * nested `ResultAsync`s. + * + * @param f The function to apply to the current value + */ andThen>( f: (t: T) => R, ): ResultAsync, InferErrTypes | E> @@ -179,6 +290,13 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Takes an `Err` value and maps it to a `Result` or `ResultAsync`. Useful for + * error recovery. + * + * @param f A function to apply to an `Err` value, leaving `Ok` values + * untouched. + */ orElse>( f: (e: E) => R, ): ResultAsync | T, InferErrTypes> @@ -199,10 +317,27 @@ export class ResultAsync implements PromiseLike> { ) } + /** + * Given 2 functions (one for the `Ok` variant and one for the `Err` variant) + * execute the function that matches the `ResultAsync` variant. + * + * Unlike `Result.match`, always returns a `Promise` because of the + * asynchronous nature of `ResultAsync`. + * + * @param ok callback for the `Ok` variant + * @param _err callback for the `Err` variant + */ match(ok: (t: T) => A, _err: (e: E) => B): Promise { return this._promise.then((res) => res.match(ok, _err)) } + /** + * Unwrap the `Ok` value, or return the default if there is an `Err`. + * + * Works like `Result.unwrapOr` but returns a `Promise` instead of `T`. + * + * @param t the default value to return if there is an `Err` + */ unwrapOr(t: A): Promise { return this._promise.then((res) => res.unwrapOr(t)) } @@ -244,21 +379,49 @@ export class ResultAsync implements PromiseLike> { } } +/** + * Constructs an `Ok` variant of `ResultAsync`. + * + * @param value the success value to wrap + */ export function okAsync(value: T): ResultAsync export function okAsync(value: void): ResultAsync export function okAsync(value: T): ResultAsync { return new ResultAsync(Promise.resolve(new Ok(value))) } +/** + * Constructs an `Err` variant of `ResultAsync`. + * + * @param err the error value to wrap + */ export function errAsync(err: E): ResultAsync export function errAsync(err: void): ResultAsync export function errAsync(err: E): ResultAsync { return new ResultAsync(Promise.resolve(new Err(err))) } +/** + * Top-level export of {@link ResultAsync.fromPromise}. + * + * Transforms a `PromiseLike` that may reject into a `ResultAsync`. + */ export const fromPromise = ResultAsync.fromPromise + +/** + * Top-level export of {@link ResultAsync.fromSafePromise}. + * + * Like `fromPromise` but does not handle rejection — use only when you know + * the promise will not reject. + */ export const fromSafePromise = ResultAsync.fromSafePromise +/** + * Top-level export of {@link ResultAsync.fromThrowable}. + * + * Wraps a promise-returning function so it returns `ResultAsync` instead of + * throwing or rejecting. + */ export const fromAsyncThrowable = ResultAsync.fromThrowable // Combines the array of async results into one result. diff --git a/src/result.ts b/src/result.ts index ad447caa..cbbed909 100644 --- a/src/result.ts +++ b/src/result.ts @@ -13,8 +13,12 @@ import { // eslint-disable-next-line @typescript-eslint/no-namespace export namespace Result { /** - * Wraps a function with a try catch, creating a new function with the same - * arguments but returning `Ok` if successful, `Err` if the function throws + * Wraps a function with a try/catch, creating a new function with the same + * arguments but returning `Ok` if successful, `Err` if the function throws. + * + * Useful when interfacing with third-party code that throws. Because the + * types of thrown errors cannot be known, prefer passing `errorFn` to map + * the thrown value to a known error type. * * @param fn function to wrap with ok on success or err on failure * @param errorFn when an error is thrown, this will wrap the error result if provided @@ -34,6 +38,17 @@ export namespace Result { } } + /** + * Combines a list of `Result`s into a single `Result`. + * + * If all results are `Ok`, returns an `Ok` containing a list of all values. + * If any result is an `Err`, short-circuits and returns the first `Err`. + * + * Works on both homogeneous and heterogeneous lists (but not mixed with + * `ResultAsync`). Conceptually similar to `Promise.all`. + * + * @param resultList list of `Result`s to combine + */ export function combine< T extends readonly [Result, ...Result[]] >(resultList: T): CombineResults @@ -46,6 +61,15 @@ export namespace Result { return combineResultList(resultList) as CombineResults } + /** + * Like {@link Result.combine} but without short-circuiting. + * + * Instead of returning only the first error, returns a list of all error + * values from failed results. There is no guarantee about the length of the + * error list if only some results fail. + * + * @param resultList list of `Result`s to combine + */ export function combineWithAllErrors< T extends readonly [Result, ...Result[]] >(resultList: T): CombineResultsWithAllErrorsArray @@ -61,12 +85,22 @@ export namespace Result { export type Result = Ok | Err +/** + * Constructs an `Ok` variant of `Result`. + * + * @param value the success value to wrap + */ export function ok(value: T): Ok export function ok(value: void): Ok export function ok(value: T): Ok { return new Ok(value) } +/** + * Constructs an `Err` variant of `Result`. + * + * @param err the error value to wrap + */ export function err(err: E): Err export function err(err: E): Err export function err(err: void): Err @@ -248,10 +282,26 @@ interface IResult { * by applying an async function to a contained `Ok` value, leaving an `Err` * value untouched. * + * Similar to `map` except the mapping function must return a `Promise`, and + * `asyncMap` returns a `ResultAsync`. + * * @param f An async function to apply an `OK` value */ asyncMap(f: (t: T) => Promise): ResultAsync + /** + * Similar to `andThrough` except you must return a `ResultAsync`. + * + * You can then chain the result using the `ResultAsync` APIs. + * + * @param f The function that returns a `ResultAsync` to apply to the current + * value + */ + asyncAndThrough>( + f: (t: T) => R, + ): ResultAsync | E> + asyncAndThrough(f: (t: T) => ResultAsync): ResultAsync + /** * Unwrap the `Ok` value, or return the default if there is an `Err` * @@ -520,6 +570,11 @@ export class Err implements IResult { } } +/** + * Top-level export of {@link Result.fromThrowable}. + * + * Wraps a throwing function so it returns `Ok` on success or `Err` on throw. + */ export const fromThrowable = Result.fromThrowable //#region Combine - Types From c2af7e3db5ca5c5e92676a86ca43c4713ab0c24a Mon Sep 17 00:00:00 2001 From: Austin Weeks <168379071+austin-weeks@users.noreply.github.com> Date: Wed, 26 Aug 2026 23:08:51 -0400 Subject: [PATCH 2/2] add changeset --- .changeset/fifty-tips-flow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fifty-tips-flow.md diff --git a/.changeset/fifty-tips-flow.md b/.changeset/fifty-tips-flow.md new file mode 100644 index 00000000..185a91c4 --- /dev/null +++ b/.changeset/fifty-tips-flow.md @@ -0,0 +1,5 @@ +--- +'neverthrow': patch +--- + +JS Docs have been added for all exported API members.