diff --git a/README.md b/README.md deleted file mode 100644 index 2abe302..0000000 --- a/README.md +++ /dev/null @@ -1,594 +0,0 @@ -# Patina - -- error- and nothing-handling library for TypeScript -- inspired by Rust's [`Result`](https://doc.rust-lang.org/std/result/) and - [`Option`](https://doc.rust-lang.org/std/option) types -- utilities for composing functions that return errors and interacting with code that throws errors -- no dependencies - -## Table of contents - -- [Installation](#installation) -- [Usage](#usage) -- [Panic](#panic) -- [Result](#result) - - [fromThrowable](#fromthrowablef-function) - - [fromPromise](#frompromisepromise-promise) - - [and](#andother-result) - - [andThen](#andthenf-function) - - [err](#err) - - [expect](#expectmessage-string) - - [expectErr](#expecterrmessage-string) - - [flatten](#flatten) - - [inspect](#inspectf-function) - - [inspectErr](#inspecterrf-function) - - [isErr](#iserr) - - [isOk](#isok) - - [map](#mapf-function) - - [mapErr](#maperrf-function) - - [mapOr](#mapordefaultvalue-t-f-function) - - [mapOrElse](#maporelsedefaultvalue-function-f-function) - - [ok](#ok) - - [or](#orother-result) - - [orElse](#orelsef-function) - - [unwrap](#unwrap) - - [unwrapErr](#unwraperr) - - [unwrapOr](#unwrapordefaultvalue-t) - - [unwrapOrElse](#unwraporelsef-function) - - [match](#matchmatcher-matcher) -- [AsyncResult](#asyncresult) -- [Option](#option) -- [AsyncOption](#asyncoption) -- [Utilities](#utilities) - - [asyncFn](#asyncfn) - - [tryBlock](#tryblock) - - [tryBlockAsync](#tryblockasync) -- [Similar Libraries](#similar-libraries) - -## Installation - -CommonJS and ESM modules are available. - -```sh -npm install @patina/core -``` - -## Usage - -```ts -import { - asyncFn, - AsyncResult, - Err, - ErrorWithCause, - None, - Ok, - Option, - Panic, - Result, - Some, -} from "@patina/core"; -import { db } from "./db"; - -function divide(a: number, b: number): Result { - if (b === 0) { - return Err(new Error("division by zero")); - } - return Ok(a / b); -} - -// You do not have to use `namespace` pattern, but I find it useful to group errors and their mappers together. -namespace DatabaseError { - export class Unreachable extends ErrorWithCause { - readonly tag = "DatabaseError.Unreachable"; - } - - export class ValidationError extends ErrorWithCause { - readonly tag = "DatabaseError.ValidationError"; - } - - export function from(error: Error): DatabaseError { - if (error.message === "validation error") { - return new ValidationError(error.message, { cause: error }); - } - if (error.message === "unreachable") { - return new Unreachable(error.message, { cause: error }); - } - // Add more error variants here, for now we panic if we encounter an unknown error - throw new Panic("unhandled database error", { cause: error }); - } -} -export type DatabaseError = DatabaseError.ValidationError | DatabaseError.Unreachable; - -// Chain API example: -function findGradesByStudentId(id: string): AsyncResult, DatabaseError> { - return Result.fromPromise(db.findGradesByStudentId(id)) - .map((grades) => (grades ? Some(grades) : None)) - .mapErr(DatabaseError.from); -} - -// Or you can use `asyncFn` to wrap functions that return `Promise>` to convert return type to `AsyncResult` -// Inferred type is `(studentId: string) => AsyncResult` -const getAverageGrade = asyncFn(async (studentId: string) => { - const grades = await findGradesByStudentId(studentId) - .andThen((maybeGrades) => { - return maybeGrades.match({ - Some: (grades) => { - if (grades.length === 0) { - return Err(new Error("grades not found")); - } - return Ok(grades); - }, - None: () => { - // Map to error if grades not found - return Err(new Error("grades not found")); - }, - }); - }) - .mapErr(() => { - // Map to generic error from database error, or handle each variant - return new Error("failed to get grades"); - }); - - // Check and return error - if (grades.isErr()) { - return Err(grades.unwrapErr()); - } - - // Safe to unwrap because we checked for error - const value = grades.unwrap(); - return divide( - value.reduce((a, b) => a + b, 0), - value.length, - ); -}); -``` - -## `Panic` - -`Panic` is a special error that extends native `Error` and represents a panic condition. It is used -to indicate that a function has failed in an unrecoverable way. The `throw` statement should only be -used to raise exceptions that represent a bug detected in your program. - -On the other hand, `Result` can represent either the successful outcome of some operation, `Ok`, -or an expected runtime error, `Err`. `Result` types are used alongside user-defined error types -that represent the various anticipated runtime failure modes that the associated operation might -encounter. Unlike exceptions, `Result` types must be explicitly handled and propagated by the -developer. - -The utilities of this library are designed to differentiate between recoverable errors and panics. -For example thrown `Panics` will not be caught: - -```ts -function panicOrError() { - const rand = Math.random(); - if (rand > 0.5) { - throw new Panic("random panic"); - } - return rand; -} - -// Will not catch the panic -const result = Result.from(() => { - panicOrError(); -}); -``` - -## `Result` - -`Result` is a type that represents either success (`Ok`) or failure (`Err`). - -`Result` is the type used for returning errors. It is a discriminated union with the variants, -`Ok`, representing success and containing a value, and `Err`, representing error and -containing an error value. - -Functions return `Result` whenever errors are expected and recoverable. - -### `.fromThrowable(f: Function)` - -Tries to execute a function and returns the result as a `Result`. - -```ts -const result = Result.from(() => { - if (Math.random() > 0.5) { - return 42; - } else { - throw new Error("random error"); - } -}); -``` - -### `.fromPromise(promise: Promise)` - -Tries to resolve a promise and returns the result as a `AsyncResult`. - -```ts -// AsyncResult -const result = Result.fromPromise(Promise.resolve(42)); -``` - -### `.and(other: Result)` - -Returns `other` if the result is `Ok`, otherwise returns `this` (as `Err`). - -### `.andThen(f: Function)` - -Calls `f` if the result is `Ok`, otherwise returns `this` (as `Err`). - -```ts -let x: Result = Ok(2); -assert.deepStrictEqual(x.andThen((n) => Ok(n * 2)).unwrap(), 4); - -let y: Result = Err("late error"); -assert.deepStrictEqual(y.andThen((n) => Ok(n * 2)).unwrapErr(), "late error"); -``` - -### `.err()` - -Returns `None` if the result is `Ok`, otherwise returns `Some` containing the error. - -### `.expect(message: string)` - -Returns the contained `Ok` value. - -Throws `Panic` if the value is an `Err`, with a message containing `message` and content of the -`Err` as `cause`. - -```ts -const x = Err("emergency failure"); -x.expect("Testing expect"); // throws Panic: Testing expect -``` - -### `.expectErr(message: string)` - -Returns the contained `Err` value. - -Throws `Panic` if the value is an `Ok`, with a message containing `message` and content of the `Ok` -as `cause`. - -```ts -const x = Ok(2); -x.expectErr("Testing expectErr"); // throws Panic: Testing expectErr -``` - -### `.flatten()` - -Converts from `Result, E>` to `Result`. - -### `.inspect(f: Function)` - -Calls the provided function with the contained value (if `Ok`). - -```ts -const x = Ok(2); -x.inspect((v) => console.log(v)); -``` - -### `.inspectErr(f: Function)` - -Calls the provided function with the contained error (if `Err`). - -```ts -const x = Err("error"); -x.inspectErr((e) => console.error(e)); -``` - -### `.isErr()` - -Returns `true` if the result is an `Err`, and narrows the type to `Err`. - -### `.isOk()` - -Returns `true` if the result is an `Ok`, and narrows the type to `Ok`. - -### `.map(f: Function)` - -Maps a `Result` to `Result` by applying a function to a contained `Ok` value, leaving an -`Err` value untouched. - -```ts -const x = Ok(10); -const mapped = x.map((n) => `number ${n}`); -assert.strictEqual(mapped.unwrap(), "number 10"); -``` - -### `.mapErr(f: Function)` - -Maps a `Result` to `Result` by applying a function to a contained `Err` value, leaving -an `Ok` value untouched. - -```ts -const x = Err("error"); -const mapped = x.mapErr((s) => s.length); -assert.strictEqual(mapped.unwrapErr(), 5); -``` - -### `.mapOr(defaultValue: T, f: Function)` - -Returns the provided default (if `Err`), or applies a function to the contained value (if `Ok`). - -```ts -const x: Result = Ok("foo"); -assert.strictEqual( - x.mapOr(42, (v) => v.length), - 3, -); - -const y: Result = Err("bar"); -assert.strictEqual( - y.mapOr(42, (v) => v.length), - 42, -); -``` - -### `.mapOrElse(defaultValue: Function, f: Function)` - -Maps a `Result` to `A | B` by applying fallback function `defaultValue` to a contained `Err` -value, or function `f` to a contained `Ok` value. - -```ts -const k = 21; - -let x: Result = Ok("foo"); -assert.strictEqual( - x.mapOrElse( - () => k * 2, - (v) => v.length, - ), - 3, -); - -x = Err("bar"); -assert.strictEqual( - x.mapOrElse( - () => k * 2, - (v) => v.length, - ), - 42, -); -``` - -### `.ok()` - -Returns `Some` if the result is `Ok`, otherwise returns `None`. - -### `.or(other: Result)` - -Returns `other` if the result is `Err`, otherwise returns `this` (as `Ok`). - -```ts -let x: Result = Ok(2); -let y: Result = Err("late error"); -assert.deepStrictEqual(x.or(y).unwrap(), 2); -assert.deepStrictEqual(y.or(x).unwrap(), 2); -``` - -### `.orElse(f: Function)` - -Calls `f` if the result is `Err`, otherwise returns `this` (as `Ok`). - -```ts -let x: Result = Ok(2); -assert.deepStrictEqual(x.orElse((e) => Err(e + "bar")).unwrap(), 2); - -let y: Result = Err("foo"); -assert.deepStrictEqual(y.orElse((e) => Err(e + "bar")).unwrapErr(), "foobar"); -``` - -### `.unwrap()` - -Returns the contained `Ok` value or `undefined`. - -This works differently from Rust's `unwrap` method, which panics if the value is an `Err`. Since -TypeScript compiler is not able to enforce that a value is checked for error before unwrapping, this -method is safer to use. - -```ts -let x: Result = Ok(2); -let value = x.unwrap(); // `value` type is `2 | undefined` -if (x.isOk()) { - value = x.unwrap(); // `value` type is `2` -} else { - value = x.unwrap(); // `value` type is `undefined` -} -``` - -### `.unwrapErr()` - -Returns the contained `Err` value or `undefined`. - -This works differently from Rust's `unwrap_err` method, which panics if the value is an `Ok`. Since -TypeScript compiler is not able to enforce that a value is checked for error before unwrapping, this -method is safer to use. - -```ts -let x: Result = Err("failure"); -let value = x.unwrapErr(); // `value` type is `"failure" | undefined` -if (x.isErr()) { - value = x.unwrapErr(); // `value` type is `"failure"` -} else { - value = x.unwrapErr(); // `value` type is `undefined` -} -``` - -### `.unwrapOr(defaultValue: T)` - -Returns the contained `Ok` value or `defaultValue`. - -```ts -let x: Result = Ok(2); -assert.strictEqual(x.unwrapOr(0), 2); - -let y: Result = Err("error"); -assert.strictEqual(y.unwrapOr(0), 0); -``` - -### `.unwrapOrElse(f: Function)` - -Returns the contained `Ok` value or the result of a function. - -```ts -let x: Result = Ok(2); -assert.strictEqual( - x.unwrapOrElse(() => 0), - 2, -); - -let y: Result = Err("error"); -assert.strictEqual( - y.unwrapOrElse(() => 0), - 0, -); -``` - -### `.match(matcher: Matcher)` - -Matches a `Result` to `A | B` by applying a matcher object. Similar to Rust's `match` -statement. - -```ts -const x: Result = Ok(2); -assert.strictEqual( - x.match({ - Ok: (v) => v * 2, - Err: (e) => e.length, - }), - 4, -); - -const y: Result = Err("error"); -assert.strictEqual( - y.match({ - Ok: (v) => v * 2, - Err: (e) => e.length, - }), - 5, -); -``` - -## `AsyncResult` - -`AsyncResult` is a type that represents either success (`Ok`) or failure (`Err`) of an asynchronous -operation. - -It implements the `Promise` interface, so you can use it as a drop-in replacement for promises. - -The same methods are available on `AsyncResult` as on `Result`. - -Methods on both `Result` and `AsyncResult` have `async` versions that accept a function that return -a `Promise`, e.g. `mapAsync`, `inspectAsync` `andThenAsync`, etc. These methods are useful for -chaining asynchronous operations and will turn a `Result` into an `AsyncResult`. - -## `Option` - -Similar methods are available on `Option` as on `Result`. - -### `.fromNullish(value: T)` - -Returns `Some` if the value is not `null` or `undefined`, otherwise returns `None`. - -```ts -const x = Option.fromNullish(42); -assert.deepStrictEqual(x.unwrap(), 42); - -const y = Option.fromNullish(null); -assert.deepStrictEqual(y, None); - -const z = Option.fromNullish(undefined); -assert.deepStrictEqual(z, None); -``` - -## `AsyncOption` - -`AsyncOption` is a type that represents either a value (`Some`) or nothing (`None`) of an -asynchronous operation. - -## Utilities - -### `isResult(value: any): value is Result` - -Returns `true` if `value` is an instance of `Result`. - -### `isAsyncResult(value: any): value is AsyncResult` - -Returns `true` if `value` is an instance of `AsyncResult`. - -### `isOption(value: any): value is Option` - -Returns `true` if `value` is an instance of `Option`. - -### `isAsyncOption(value: any): value is AsyncOption` - -Returns `true` if `value` is an instance of `AsyncOption`. - -### `asyncFn` - -Wraps a function that returns any shape of `Promise>` and wraps the return value in -a `AsyncResult`. - -```ts -// (a: number, b: number) => Promise | Ok> -const divide = async (a: number, b: number) => (b === 0 ? Err("division by zero") : Ok(a / b)); - -// (a: number, b: number) => AsyncResult -const wrapped = asyncFn(divide); - -// then you can await the result -const result = await wrapped(1, 2); // => Result -``` - -### `tryBlock` - -Creates a scope where you can use `yield*` and `try()` together to unwrap or propagate errors from a -`Result`. This is trying to emulate Rust's -[`try_blocks`](https://doc.rust-lang.org/stable/unstable-book/language-features/try-blocks.html) and -[`?` operator](https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html). Only works -with synchronous blocks, if you need to use asynchronous operations, use `tryBlockAsync` instead. - -```ts -const result = tryBlock(function* () { - const x = yield* Ok(1).try(); - const y = yield* Ok(2).try(); - return Ok(x + y); -}); -assert.equal(result.unwrap(), 3); -``` - -### `tryBlockAsync` - -Creates a scope where you can use `yield*` and `try()` together to unwrap or propagate errors from a -`Result` or `AsyncResult`. This is trying to emulate Rust's -[`try_blocks`](https://doc.rust-lang.org/stable/unstable-book/language-features/try-blocks.html) and -[`?` operator](https://doc.rust-lang.org/rust-by-example/std/result/question_mark.html). - -```ts -const asyncNumber = new AsyncResult(Promise.resolve(Ok(2))); - -const result = await tryBlockAsync(async function* () { - const x = yield* Ok(1).try(); - const y = yield* asyncNumber.try(); - return Ok(x + y); -}); -assert.equal(result.unwrap(), 3); -``` - -## Similar Libraries - -Unfortunately, there are no native TypeScript statements or expression that can help you deal with -or propagate errors and you can only go so far with wrapping `try` and `catch`. Writing code with -this library will look very different to what you're used to. Although it may seem verbose, it is -more explicit and safer, but on the other hand it may not be straightforward to use and sometimes -you can find yourself fighting the type system. - -If you find this library lacking, you may want to check out similar libraries: - -- [@badrap/result](https://github.com/badrap/result) -- [effect](https://github.com/Effect-TS/effect) -- [neverthrow](https://github.com/supermacro/neverthrow) -- [option-t](https://github.com/option-t/option-t) -- [optionals](https://github.com/OliverBrotchie/optionals) -- [oxide.ts](https://github.com/traverse1984/oxide.ts) -- [qio](https://github.com/tusharmath/qio) -- [true-myth](https://github.com/true-myth/true-myth) -- [ts-results](https://github.com/vultix/ts-results) -- [ts-results-es](https://github.com/lune-climate/ts-results-es) diff --git a/benchmarks/tryblock.bench.ts b/benchmarks/tryblock.bench.ts index caf9e61..e3df143 100644 --- a/benchmarks/tryblock.bench.ts +++ b/benchmarks/tryblock.bench.ts @@ -1,23 +1,28 @@ -import { asyncFn } from "../src/fn.ts"; import { Ok, type Result } from "../src/result.ts"; +import { runAsync } from "../src/run.ts"; import { tryBlockAsync } from "../src/try.ts"; -// deno-lint-ignore require-await -const getOne = asyncFn(async (): Promise> => Ok(1)); +function getOne() { + return runAsync(async (): Promise> => { + return Ok(1); + }); +} function chain() { return getOne().map((n) => n + 1); } -const af = asyncFn(async () => { - const one = await getOne(); - if (one.isErr()) { - return one; - } - return Ok(one.expect("ok") + 1); -}); +function runner() { + return runAsync(async (): Promise> => { + const one = await getOne(); + if (one.isErr()) { + return one; + } + return Ok(one.unwrap() + 1); + }); +} -function tba() { +function block() { return tryBlockAsync(async function* () { const one = yield* getOne(); return Ok(one + 1); @@ -33,15 +38,15 @@ Deno.bench({ }); Deno.bench({ - name: "asyncFn", + name: "runner", fn: async () => { - await af(); + await runner(); }, }); Deno.bench({ name: "tryBlockAsync", fn: async () => { - await tba(); + await block(); }, }); diff --git a/example/main.ts b/example/main.ts index faf95a1..0dc3e19 100644 --- a/example/main.ts +++ b/example/main.ts @@ -1,6 +1,6 @@ import { db } from "./db.ts"; import { Err, Ok, Result } from "../src/result.ts"; -import { AsyncResult } from "../src/async_result.ts"; +import { ResultAsync } from "../src/result_async.ts"; import { asyncFn } from "../src/fn.ts"; import { None, Option, Some } from "../src/option.ts"; import { ErrorWithCause, Panic } from "../src/error.ts"; @@ -37,14 +37,14 @@ namespace DatabaseError { export type DatabaseError = DatabaseError.ValidationError | DatabaseError.Unreachable; // Chain API example: -function findGradesByStudentId(id: string): AsyncResult, DatabaseError> { +function findGradesByStudentId(id: string): ResultAsync, DatabaseError> { return Result.fromPromise(db.findGradesByStudentId(id)) .map((grades) => (grades ? Some(grades) : None)) .mapErr(DatabaseError.from); } -// Or you can use `asyncFn` to wrap functions that return `Promise>` to convert return type to `AsyncResult` -// Inferred type is `(studentId: string) => AsyncResult` +// Or you can use `asyncFn` to wrap functions that return `Promise>` to convert return type to `ResultAsync` +// Inferred type is `(studentId: string) => ResultAsync` // @ts-ignore const getAverageGrade = asyncFn(async (studentId: string) => { const grades = await findGradesByStudentId(studentId) diff --git a/src/async_helpers.ts b/src/async_helpers.ts deleted file mode 100644 index facaaa1..0000000 --- a/src/async_helpers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AsyncResult } from "./async_result.ts"; -import { AsyncOption } from "./async_option.ts"; -import { Err, Ok } from "./result.ts"; -import { None, Some } from "./option.ts"; - -export function AsyncOk(value: T): AsyncResult { - return new AsyncResult(Promise.resolve(Ok(value))); -} - -export function AsyncErr(error: E): AsyncResult { - return new AsyncResult(Promise.resolve(Err(error))); -} - -export function AsyncSome(value: T): AsyncOption { - return new AsyncOption(Promise.resolve(Some(value))); -} - -export const AsyncNone: AsyncOption = new AsyncOption(Promise.resolve(None)); diff --git a/src/fn.ts b/src/fn.ts deleted file mode 100644 index 0a2d7f7..0000000 --- a/src/fn.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Functions wrappers that infer the return type of a function as a `Result`. - * - * @module - */ - -import type { Result } from "./result.ts"; -import { AsyncResult } from "./async_result.ts"; -import type { InferErr, InferOk } from "./util.ts"; - -/** - * Wraps a function that returns any shape of `Promise>` or `AsyncResult` - * and wraps the return value in an `AsyncResult`. - * - * This is useful because you do not need to manually convert `Ok` and `Err` types to their async versions and you'll also get a nice `AsyncResult` type instead of a union type. - * - * @param f - The function to wrap. - * @returns A function that returns an `AsyncResult`. - * - * @example - * ``` - * // instead of `Promise | Ok>` return type, - * // you get `AsyncResult` - * const getUser = asyncFn(async (userId: string) => { - * const user = await findUserById(userId) - * if (!user) { - * return Err("user not found") - * } - * return Ok(user) - * }) - * ``` - */ -export function asyncFn< - A extends any[], - R extends AsyncResult | Promise>, ->( - f: (...args: A) => R, -): (...args: A) => AsyncResult>, InferErr>> { - return function (...args: A): AsyncResult>, InferErr>> { - return new AsyncResult(f(...args)); - }; -} diff --git a/src/mod.ts b/src/mod.ts index 8fed756..76ca407 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -6,13 +6,12 @@ * @module */ -export * from "./async_helpers.ts"; -export * from "./async_option.ts"; -export * from "./async_result.ts"; export * from "./error.ts"; -export * from "./fn.ts"; -export { None, Option, type OptionMatch, type OptionMatchAsync, Some } from "./option.ts"; -export { Err, Ok, Result, type ResultMatch, type ResultMatchAsync } from "./result.ts"; +export * from "./none_async.ts"; +export * from "./option_async.ts"; +export * from "./option.ts"; +export * from "./result_async.ts"; +export * from "./result.ts"; export * from "./run.ts"; export * from "./try.ts"; export * from "./unwind.ts"; diff --git a/src/none_async.ts b/src/none_async.ts new file mode 100644 index 0000000..7bfb474 --- /dev/null +++ b/src/none_async.ts @@ -0,0 +1,6 @@ +// NoneAsync alone is defined here to avoid circular dependency between option.ts and option_async.ts + +import { None } from "./option.ts"; +import { OptionAsync } from "./option_async.ts"; + +export const NoneAsync: OptionAsync = new OptionAsync(Promise.resolve(None)); diff --git a/src/option.ts b/src/option.ts index ac3d0ac..7dc5f18 100644 --- a/src/option.ts +++ b/src/option.ts @@ -3,8 +3,8 @@ * @module */ -import { AsyncOption } from "./async_option.ts"; -import { AsyncResult } from "./async_result.ts"; +import { OptionAsync } from "./option_async.ts"; +import { ResultAsync } from "./result_async.ts"; import { Panic } from "./error.ts"; import { Err, Ok, type Result } from "./result.ts"; @@ -39,7 +39,7 @@ export type OptionMatchAsync = { const unwrapSymbol = Symbol("unwrap"); /** - * @internal The internal implementation of `Option`. + * @internal The internal implementation of the `Option` union type. */ export class OptionImpl { private readonly _some: boolean; @@ -301,7 +301,7 @@ export class OptionImpl { } /** - * Maps an `Option` to `AsyncOption` by applying an async function to a contained value. + * Maps an `Option` to `OptionAsync` by applying an async function to a contained value. * * @param f - The async function to apply to the contained value. * @returns The result of the async function application. @@ -316,11 +316,11 @@ export class OptionImpl { * assertEquals(await x.mapAsync(async (s) => s.length), None) * ``` */ - public mapAsync(f: (value: T) => Promise): AsyncOption { + public mapAsync(f: (value: T) => Promise): OptionAsync { if (this._some) { - return new AsyncOption(f(this._value as T).then(Some)); + return new OptionAsync(f(this._value as T).then(Some)); } - return new AsyncOption(Promise.resolve(None)); + return new OptionAsync(Promise.resolve(None)); } /** @@ -372,11 +372,11 @@ export class OptionImpl { * await Option.fromNullish(list[5]).inspectAsync(async (x) => console.log(`got: ${x}`)) * ``` */ - public inspectAsync(f: (value: T) => Promise): AsyncOption { + public inspectAsync(f: (value: T) => Promise): OptionAsync { if (this._some) { - return new AsyncOption(f(this._value as T).then(() => this as unknown as Some)); + return new OptionAsync(f(this._value as T).then(() => this as unknown as Some)); } - return new AsyncOption(Promise.resolve(this as unknown as Some)); + return new OptionAsync(Promise.resolve(this as unknown as Some)); } /** @@ -542,7 +542,7 @@ export class OptionImpl { } /** - * Transforms the `Option` into a `AsyncResult`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. + * Transforms the `Option` into a `ResultAsync`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. * * @param err - The async function to compute the error to return if the option is `None`. * @returns The result of the transformation. @@ -556,11 +556,11 @@ export class OptionImpl { * assertEquals(await x.okOrElseAsync(async () => 0), Err(0)) * ``` */ - public okOrElseAsync(f: () => Promise): AsyncResult { + public okOrElseAsync(f: () => Promise): ResultAsync { if (this._some) { - return new AsyncResult(Promise.resolve(Ok(this._value as T))); + return new ResultAsync(Promise.resolve(Ok(this._value as T))); } - return new AsyncResult(f().then((err) => Err(err))); + return new ResultAsync(f().then((err) => Err(err))); } /** @@ -668,11 +668,11 @@ export class OptionImpl { * assertEquals(item20, None) * ``` */ - public andThenAsync(f: (value: T) => Promise> | AsyncOption): AsyncOption { + public andThenAsync(f: (value: T) => Promise> | OptionAsync): OptionAsync { if (this._some) { - return new AsyncOption(f(this._value as T)); + return new OptionAsync(f(this._value as T)); } - return new AsyncOption(Promise.resolve(None)); + return new OptionAsync(Promise.resolve(None)); } /** @@ -726,15 +726,15 @@ export class OptionImpl { * assertEquals(await Some(4).filterAsync(isEven), Some(4)) * ``` */ - public filterAsync(predicate: (value: T) => Promise): AsyncOption { + public filterAsync(predicate: (value: T) => Promise): OptionAsync { if (this._some) { - return new AsyncOption( + return new OptionAsync( predicate(this._value as T).then((result) => result ? this as unknown as Option : None ), ); } - return new AsyncOption(Promise.resolve(None)); + return new OptionAsync(Promise.resolve(None)); } /** @@ -808,11 +808,11 @@ export class OptionImpl { * assertEquals(await None.orElseAsync(nobody), None) * ``` */ - public orElseAsync(f: () => Promise> | AsyncOption): AsyncOption { + public orElseAsync(f: () => Promise> | OptionAsync): OptionAsync { if (this._some) { - return new AsyncOption(Promise.resolve(this as unknown as Option)); + return new OptionAsync(Promise.resolve(this as unknown as Option)); } - return new AsyncOption(f()); + return new OptionAsync(f()); } /** diff --git a/src/async_option.ts b/src/option_async.ts similarity index 69% rename from src/async_option.ts rename to src/option_async.ts index 679382d..ce2ed2f 100644 --- a/src/async_option.ts +++ b/src/option_async.ts @@ -1,55 +1,55 @@ /** - * This module contains the `AsyncOption` class, which is a promise that resolves to an `Option`. + * This module contains the `OptionAsync` class, which is a promise that resolves to an `Option`. * @module */ -import { AsyncResult } from "./async_result.ts"; -import type { Option, OptionMatch, OptionMatchAsync } from "./option.ts"; +import { ResultAsync } from "./result_async.ts"; +import { type Option, type OptionMatch, type OptionMatchAsync, Some } from "./option.ts"; type OptionPromiseType = | Promise> | PromiseLike> - | AsyncOption; + | OptionAsync; /** * A promise that resolves to an `Option`. * * This class is useful for chaining multiple asynchronous operations that return an `Option`. */ -export class AsyncOption implements PromiseLike> { +export class OptionAsync implements PromiseLike> { /** * The promise that resolves to an `Option`. */ public readonly promise: OptionPromiseType; /** - * Creates a new `AsyncOption`. + * Creates a new `OptionAsync`. * @param promise - The promise that resolves to an `Option`. */ public constructor(promise: OptionPromiseType) { this.promise = promise; } - public get [Symbol.toStringTag](): "AsyncOption" { - return "AsyncOption"; + public get [Symbol.toStringTag](): "OptionAsync" { + return "OptionAsync"; } /** - * Converts the `AsyncOption` to a JSON object. + * Converts the `OptionAsync` to a JSON object. */ - public toJSON(): { AsyncOption: Promise> | PromiseLike> | AsyncOption } { - return { AsyncOption: this.promise }; + public toJSON(): { OptionAsync: Promise> | PromiseLike> | OptionAsync } { + return { OptionAsync: this.promise }; } /** - * Converts the `AsyncOption` to a string. + * Converts the `OptionAsync` to a string. */ public toString(): string { - return `AsyncOption(${this.promise.toString()})`; + return `OptionAsync(${this.promise.toString()})`; } public [Symbol.for("nodejs.util.inspect.custom")](): { - AsyncOption: OptionPromiseType; + OptionAsync: OptionPromiseType; } { return this.toJSON(); } @@ -79,20 +79,20 @@ export class AsyncOption implements PromiseLike> { } /** - * Matches the `AsyncOption` to its content. + * Matches the `OptionAsync` to its content. * - * @param matcher - The matcher to match the `AsyncOption` against. + * @param matcher - The matcher to match the `OptionAsync` against. * @returns The result of the match. * * @example * ``` - * const x = await AsyncSome(0).match({ + * const x = await SomeAsync(0).match({ * Some: (value) => value + 1, * None: () => 0 * }) * assertEquals(x, 1) * - * const y = await AsyncNone.match({ + * const y = await NoneAsync.match({ * Some: (value) => value + 1, * None: () => 0 * }) @@ -104,20 +104,20 @@ export class AsyncOption implements PromiseLike> { } /** - * Matches the `AsyncOption` to its content asynchronously. + * Matches the `OptionAsync` to its content asynchronously. * - * @param matcher - The matcher to match the `AsyncOption` against. + * @param matcher - The matcher to match the `OptionAsync` against. * @returns The result of the match. * * @example * ``` - * const x = await AsyncSome(0).matchAsync({ + * const x = await SomeAsync(0).matchAsync({ * Some: async (value) => value + 1, * None: async () => 0 * }) * assertEquals(x, 1) * - * const y = await AsyncNone.matchAsync({ + * const y = await NoneAsync.matchAsync({ * Some: async (value) => value + 1, * None: async () => 0 * }) @@ -129,7 +129,7 @@ export class AsyncOption implements PromiseLike> { } /** - * Transforms the `AsyncOption` into a `AsyncResult`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. + * Transforms the `OptionAsync` into a `ResultAsync`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. * * @param err - The error to return if the option is `None`. * @returns The result of the transformation. @@ -143,12 +143,12 @@ export class AsyncOption implements PromiseLike> { * assertEquals(x.okOr(0), Err(0)) * ``` */ - public okOr(err: E): AsyncResult { - return new AsyncResult(this.then((option) => option.okOr(err))); + public okOr(err: E): ResultAsync { + return new ResultAsync(this.then((option) => option.okOr(err))); } /** - * Transforms the `AsyncOption` into a `Result`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. + * Transforms the `OptionAsync` into a `Result`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. * * @param err - The function to compute the error to return if the option is `None`. * @returns The result of the transformation. @@ -162,27 +162,27 @@ export class AsyncOption implements PromiseLike> { * assertEquals(x.okOrElse(() => 0), Err(0)) * ``` */ - public okOrElse(err: () => E): AsyncResult { - return new AsyncResult(this.then((option) => option.okOrElse(err))); + public okOrElse(err: () => E): ResultAsync { + return new ResultAsync(this.then((option) => option.okOrElse(err))); } /** - * Converts the `AsyncOption` to an `AsyncResult` with an error value. + * Converts the `OptionAsync` to an `ResultAsync` with an error value. * * @param err - The function to compute the error value. - * @returns The `AsyncResult`. + * @returns The `ResultAsync`. * * @example * ``` - * const x = await AsyncSome(0).okOrElseAsync(() => Promise.resolve("error")) + * const x = await SomeAsync(0).okOrElseAsync(() => Promise.resolve("error")) * assertEquals(x, Ok(0)) * - * const y = await AsyncNone.okOrElseAsync(() => Promise.resolve("error")) + * const y = await NoneAsync.okOrElseAsync(() => Promise.resolve("error")) * assertEquals(y, Err("error")) * ``` */ - public okOrElseAsync(err: () => Promise): AsyncResult { - return new AsyncResult(this.then((option) => option.okOrElseAsync(err))); + public okOrElseAsync(err: () => Promise): ResultAsync { + return new ResultAsync(this.then((option) => option.okOrElseAsync(err))); } /** @@ -193,25 +193,25 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * let x = AsyncSome(2) - * let y = AsyncNone + * let x = SomeAsync(2) + * let y = NoneAsync * assertEquals(await x.and(y), None) * - * let x = AsyncNone - * let y = AsyncSome("foo") + * let x = NoneAsync + * let y = SomeAsync("foo") * assertEquals(await x.and(y), None) * - * let x = AsyncSome(2) - * let y = AsyncSome("foo") + * let x = SomeAsync(2) + * let y = SomeAsync("foo") * assertEquals(await x.and(y), Some("foo")) * - * let x = AsyncNone - * let y = AsyncNone + * let x = NoneAsync + * let y = NoneAsync * assertEquals(await x.and(y), None) * ``` */ - public and(other: AsyncOption): AsyncOption { - return new AsyncOption( + public and(other: OptionAsync): OptionAsync { + return new OptionAsync( this.then((option) => other.then((otherOption) => option.and(otherOption))), ); } @@ -231,13 +231,13 @@ export class AsyncOption implements PromiseLike> { * return x === 0 ? None : Some((100 / x).toString()) * } * - * assertEquals(await AsyncSome(2).andThen(divideThenToString), Some("50")) - * assertEquals(await AsyncSome(0).andThen(divideThenToString), None) // division by zero! - * assertEquals(await AsyncNone.andThen(divideThenToString), None) + * assertEquals(await SomeAsync(2).andThen(divideThenToString), Some("50")) + * assertEquals(await SomeAsync(0).andThen(divideThenToString), None) // division by zero! + * assertEquals(await NoneAsync.andThen(divideThenToString), None) * ``` */ - public andThen(f: (value: T) => Option): AsyncOption { - return new AsyncOption(this.then((option) => option.andThen((value) => f(value)))); + public andThen(f: (value: T) => Option): OptionAsync { + return new OptionAsync(this.then((option) => option.andThen((value) => f(value)))); } /** @@ -255,13 +255,13 @@ export class AsyncOption implements PromiseLike> { * return x === 0 ? None : Some((100 / x).toString()) * } * - * assertEquals(await AsyncSome(2).andThenAsync(divideThenToString), Some("50")) - * assertEquals(await AsyncSome(0).andThenAsync(divideThenToString), None) // division by zero! - * assertEquals(await AsyncNone.andThenAsync(divideThenToString), None) + * assertEquals(await SomeAsync(2).andThenAsync(divideThenToString), Some("50")) + * assertEquals(await SomeAsync(0).andThenAsync(divideThenToString), None) // division by zero! + * assertEquals(await NoneAsync.andThenAsync(divideThenToString), None) * ``` */ - public andThenAsync(f: (value: T) => Promise> | AsyncOption): AsyncOption { - return new AsyncOption(this.then((option) => option.andThenAsync(f))); + public andThenAsync(f: (value: T) => Promise> | OptionAsync): OptionAsync { + return new OptionAsync(this.then((option) => option.andThenAsync(f))); } /** @@ -276,8 +276,8 @@ export class AsyncOption implements PromiseLike> { * ``` * const list = [1, 2, 3] * - * function findItem(index: number): AsyncOption { - * return index < list.length ? AsyncSome(list[index]) : AsyncNone + * function findItem(index: number): OptionAsync { + * return index < list.length ? SomeAsync(list[index]) : NoneAsync * } * * // prints "got: 2" @@ -289,8 +289,8 @@ export class AsyncOption implements PromiseLike> { * await findItem(5).inspect((x) => console.log(`got: ${x}`)) * ``` */ - public inspect(f: (value: T) => void): AsyncOption { - return new AsyncOption(this.then((option) => option.inspect(f))); + public inspect(f: (value: T) => void): OptionAsync { + return new OptionAsync(this.then((option) => option.inspect(f))); } /** @@ -305,8 +305,8 @@ export class AsyncOption implements PromiseLike> { * ``` * const list = [1, 2, 3] * - * function findItem(index: number): AsyncOption { - * return index < list.length ? AsyncSome(list[index]) : AsyncNone + * function findItem(index: number): OptionAsync { + * return index < list.length ? SomeAsync(list[index]) : NoneAsync * } * * // prints "got: 2" @@ -318,8 +318,8 @@ export class AsyncOption implements PromiseLike> { * await findItem(5).inspectAsync(async (x) => console.log(`got: ${x}`)) * ``` */ - public inspectAsync(f: (value: T) => Promise): AsyncOption { - return new AsyncOption(this.then((option) => option.inspectAsync(f))); + public inspectAsync(f: (value: T) => Promise): OptionAsync { + return new OptionAsync(this.then((option) => option.inspectAsync(f))); } public async expect(message: string): Promise { @@ -331,7 +331,7 @@ export class AsyncOption implements PromiseLike> { * - `Some(t)` if predicate returns `true` (where t is the wrapped value), and * - `None` if predicate returns `false`. * - * This function works similar to `Array.prototype.filter()`. You can imagine the `AsyncOption` being + * This function works similar to `Array.prototype.filter()`. You can imagine the `OptionAsync` being * an array over one or zero elements. `filter()` lets you decide which elements to keep. * * @param predicate - The predicate to apply to the contained value. @@ -343,13 +343,13 @@ export class AsyncOption implements PromiseLike> { * return n % 2 === 0 * } * - * assertEquals(await AsyncNone.filter(isEven), None) - * assertEquals(await AsyncSome(3).filter(isEven), None) - * assertEquals(await AsyncSome(4).filter(isEven), Some(4)) + * assertEquals(await NoneAsync.filter(isEven), None) + * assertEquals(await SomeAsync(3).filter(isEven), None) + * assertEquals(await SomeAsync(4).filter(isEven), Some(4)) * ``` */ - public filter(f: (value: T) => boolean): AsyncOption { - return new AsyncOption(this.then((option) => option.filter(f))); + public filter(f: (value: T) => boolean): OptionAsync { + return new OptionAsync(this.then((option) => option.filter(f))); } /** @@ -369,75 +369,75 @@ export class AsyncOption implements PromiseLike> { * return n % 2 === 0 * } * - * assertEquals(await AsyncNone.filterAsync(isEven), None) - * assertEquals(await AsyncSome(3).filterAsync(isEven), None) - * assertEquals(await AsyncSome(4).filterAsync(isEven), Some(4)) + * assertEquals(await NoneAsync.filterAsync(isEven), None) + * assertEquals(await SomeAsync(3).filterAsync(isEven), None) + * assertEquals(await SomeAsync(4).filterAsync(isEven), Some(4)) * ``` */ - public filterAsync(f: (value: T) => Promise): AsyncOption { - return new AsyncOption(this.then((option) => option.filterAsync(f))); + public filterAsync(f: (value: T) => Promise): OptionAsync { + return new OptionAsync(this.then((option) => option.filterAsync(f))); } /** - * Converts from `AsyncOption>` to `AsyncOption`. + * Converts from `OptionAsync>` to `OptionAsync`. * - * @returns A flattened `AsyncOption`. + * @returns A flattened `OptionAsync`. * * @example * ``` * // Basic usage: - * let x: AsyncOption> = AsyncSome(Some(6)) + * let x: OptionAsync> = SomeAsync(Some(6)) * assertEquals(await x.flatten(), Some(6)) * - * let x: AsyncOption> = AsyncSome(None) + * let x: OptionAsync> = SomeAsync(None) * assertEquals(await x.flatten(), None) * - * let x: AsyncOption> = AsyncNone + * let x: OptionAsync> = NoneAsync * assertEquals(await x.flatten(), None) * ``` */ - public flatten(this: AsyncOption>): AsyncOption { - return new AsyncOption(this.then((option) => option.flatten())); + public flatten(this: OptionAsync>): OptionAsync { + return new OptionAsync(this.then((option) => option.flatten())); } /** - * Maps an `AsyncOption` to `AsyncOption` by applying a function to a contained value. + * Maps an `OptionAsync` to `OptionAsync` by applying a function to a contained value. * * @param f - The function to apply to the contained value. * @returns The result of the function application. * * @example * ``` - * let maybeSomeString = AsyncSome("Hello, World!") + * let maybeSomeString = SomeAsync("Hello, World!") * let maybeSomeLen = await maybeSomeString.map((s) => s.length) * assertEquals(maybeSomeLen, Some(13)) * - * let x = AsyncNone + * let x = NoneAsync * assertEquals(await x.map((s) => s.length), None) * ``` */ - public map(f: (value: T) => U): AsyncOption { - return new AsyncOption(this.then((option) => option.map(f))); + public map(f: (value: T) => U): OptionAsync { + return new OptionAsync(this.then((option) => option.map(f))); } /** - * Maps an `AsyncOption` to `AsyncOption` by applying an async function to a contained value. + * Maps an `OptionAsync` to `OptionAsync` by applying an async function to a contained value. * * @param f - The async function to apply to the contained value. * @returns The result of the async function application. * * @example * ``` - * let maybeSomeString = AsyncSome("Hello, World!") + * let maybeSomeString = SomeAsync("Hello, World!") * let maybeSomeLen = await maybeSomeString.mapAsync(async (s) => s.length) * assertEquals(maybeSomeLen, Some(13)) * - * let x = AsyncNone + * let x = NoneAsync * assertEquals(await x.mapAsync(async (s) => s.length), None) * ``` */ - public mapAsync(f: (value: T) => Promise): AsyncOption { - return new AsyncOption(this.then((option) => option.mapAsync(f))); + public mapAsync(f: (value: T) => Promise): OptionAsync { + return new OptionAsync(this.then((option) => option.mapAsync(f))); } /** @@ -449,7 +449,7 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * let x = AsyncSome("foo") + * let x = SomeAsync("foo") * assertEquals(await x.mapOr(42, v => v.length), 3) * * let x = None @@ -469,10 +469,10 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * let x = AsyncSome("foo") + * let x = SomeAsync("foo") * assertEquals(await x.mapOrAsync(42, async (v) => v.length), 3) * - * let x = AsyncNone + * let x = NoneAsync * assertEquals(await x.mapOrAsync(42, async (v) => v.length), 42) * ``` */ @@ -532,10 +532,10 @@ export class AsyncOption implements PromiseLike> { * ``` * const k = 21 * - * let x = AsyncSome("foo") + * let x = SomeAsync("foo") * assertEquals(await x.mapOrElseAsync(async () => 2 * k, async (v) => v.length), 3) * - * let x = AsyncNone + * let x = NoneAsync * assertEquals(await x.mapOrElseAsync(async () => 2 * k, async (v) => v.length), 42) * ``` */ @@ -554,25 +554,25 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * let x = AsyncSome(2) - * let y = AsyncNone + * let x = SomeAsync(2) + * let y = NoneAsync * assertEquals(await x.or(y), Some(2)) * - * let x = AsyncNone - * let y = AsyncSome(100) + * let x = NoneAsync + * let y = SomeAsync(100) * assertEquals(await x.or(y), Some(100)) * - * let x = AsyncSome(2) - * let y = AsyncSome(100) + * let x = SomeAsync(2) + * let y = SomeAsync(100) * assertEquals(await x.or(y), Some(2)) * - * let x = AsyncNone - * let y = AsyncNone + * let x = NoneAsync + * let y = NoneAsync * assertEquals(await x.or(y), None) * ``` */ - public or(other: AsyncOption): AsyncOption { - return new AsyncOption( + public or(other: OptionAsync): OptionAsync { + return new OptionAsync( this.then((thisOption) => other.then((otherOption) => thisOption.or(otherOption))), ); } @@ -588,13 +588,13 @@ export class AsyncOption implements PromiseLike> { * function nobody(): Option { return None } * function vikings(): Option { return Some("vikings") } * - * assertEquals(await AsyncSome("barbarians").orElse(vikings), Some("barbarians")) - * assertEquals(await AsyncNone.orElse(vikings), Some("vikings")) - * assertEquals(await AsyncNone.orElse(nobody), None) + * assertEquals(await SomeAsync("barbarians").orElse(vikings), Some("barbarians")) + * assertEquals(await NoneAsync.orElse(vikings), Some("vikings")) + * assertEquals(await NoneAsync.orElse(nobody), None) * ``` */ - public orElse(f: () => Option): AsyncOption { - return new AsyncOption(this.then((thisOption) => thisOption.orElse(() => f()))); + public orElse(f: () => Option): OptionAsync { + return new OptionAsync(this.then((thisOption) => thisOption.orElse(() => f()))); } /** @@ -608,13 +608,13 @@ export class AsyncOption implements PromiseLike> { * async function nobody(): Promise> { return None } * async function vikings(): Promise> { return Some("vikings") } * - * assertEquals(await AsyncSome("barbarians").orElseAsync(vikings), Some("barbarians")) - * assertEquals(await AsyncNone.orElseAsync(vikings), Some("vikings")) - * assertEquals(await AsyncNone.orElseAsync(nobody), None) + * assertEquals(await SomeAsync("barbarians").orElseAsync(vikings), Some("barbarians")) + * assertEquals(await NoneAsync.orElseAsync(vikings), Some("vikings")) + * assertEquals(await NoneAsync.orElseAsync(nobody), None) * ``` */ - public orElseAsync(f: () => Promise> | AsyncOption): AsyncOption { - return new AsyncOption(this.then((thisOption) => thisOption.orElseAsync(() => f()))); + public orElseAsync(f: () => Promise> | OptionAsync): OptionAsync { + return new OptionAsync(this.then((thisOption) => thisOption.orElseAsync(() => f()))); } /** @@ -627,8 +627,8 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * assertEquals(await AsyncSome("car").unwrapOr("bike"), "car") - * assertEquals(await AsyncNone.unwrapOr("bike"), "bike") + * assertEquals(await SomeAsync("car").unwrapOr("bike"), "car") + * assertEquals(await NoneAsync.unwrapOr("bike"), "bike") * ``` */ public async unwrapOr(defaultValue: U): Promise { @@ -646,7 +646,7 @@ export class AsyncOption implements PromiseLike> { * @example * ``` * let k = 10 - * assertEquals(await AsyncSome(4).unwrapOrElse(() => 2 * k), 4) + * assertEquals(await SomeAsync(4).unwrapOrElse(() => 2 * k), 4) * assertEquals(None.unwrapOrElse(() => 2 * k), 20) * ``` */ @@ -665,8 +665,8 @@ export class AsyncOption implements PromiseLike> { * @example * ``` * let k = 10 - * assertEquals(await AsyncSome(4).unwrapOrElseAsync(async () => 2 * k), 4) - * assertEquals(await AsyncNone.unwrapOrElseAsync(async () => 2 * k), 20) + * assertEquals(await SomeAsync(4).unwrapOrElseAsync(async () => 2 * k), 4) + * assertEquals(await NoneAsync.unwrapOrElseAsync(async () => 2 * k), 20) * ``` */ public async unwrapOrElseAsync(f: () => Promise): Promise { @@ -681,26 +681,30 @@ export class AsyncOption implements PromiseLike> { * * @example * ``` - * let x = AsyncSome(2) - * let y = AsyncNone + * let x = SomeAsync(2) + * let y = NoneAsync * assertEquals(await x.xor(y), Some(2)) * - * let x = AsyncNone - * let y = AsyncSome(2) + * let x = NoneAsync + * let y = SomeAsync(2) * assertEquals(await x.xor(y), Some(2)) * - * let x = AsyncSome(2) - * let y = AsyncSome(2) + * let x = SomeAsync(2) + * let y = SomeAsync(2) * assertEquals(await x.xor(y), None) * - * let x = AsyncNone - * let y = AsyncNone + * let x = NoneAsync + * let y = NoneAsync * assertEquals(await x.xor(y), None) * ``` */ - public xor(other: AsyncOption): AsyncOption { - return new AsyncOption( + public xor(other: OptionAsync): OptionAsync { + return new OptionAsync( this.then((thisOption) => other.then((otherOption) => thisOption.xor(otherOption))), ); } } + +export function SomeAsync(value: T): OptionAsync { + return new OptionAsync(Promise.resolve(Some(value))); +} diff --git a/src/result.ts b/src/result.ts index 8fb805a..65ccde4 100644 --- a/src/result.ts +++ b/src/result.ts @@ -1,6 +1,6 @@ import { Panic, parseError } from "./error.ts"; import { None, type Option, Some } from "./option.ts"; -import { AsyncResult } from "./async_result.ts"; +import { ResultAsync } from "./result_async.ts"; import type { InferErr, InferOk } from "./util.ts"; export type ResultMatch = { @@ -16,6 +16,9 @@ export type ResultMatchAsync = { const unwrapSymbol = Symbol("unwrap"); const unwrapErrSymbol = Symbol("unwrapErr"); +/** + * @internal Internal implementation class for the `Result` union type. + */ export class ResultImpl { private readonly _ok: boolean; private readonly _value: T | E; @@ -308,13 +311,13 @@ export class ResultImpl { } /** - * Maps a `Result` to `AsyncResult` by applying an async function to a contained `Ok` value, + * Maps a `Result` to `ResultAsync` by applying an async function to a contained `Ok` value, * leaving an `Err` value untouched. * * This function can be used to compose the results of two functions. * * @param f - The async function to apply to the contained value - * @returns A new AsyncResult with the async function applied to the contained value if `Ok`, + * @returns A new ResultAsync with the async function applied to the contained value if `Ok`, * or the original error if `Err` * * @example @@ -338,11 +341,11 @@ export class ResultImpl { * } * ``` */ - public mapAsync(f: (value: T) => Promise): AsyncResult { + public mapAsync(f: (value: T) => Promise): ResultAsync { if (this._ok) { - return new AsyncResult(f(this._value as T).then((value) => Ok(value))); + return new ResultAsync(f(this._value as T).then((value) => Ok(value))); } - return new AsyncResult(Promise.resolve(Err(this._value as E))); + return new ResultAsync(Promise.resolve(Err(this._value as E))); } /** @@ -479,13 +482,13 @@ export class ResultImpl { } /** - * Maps a `Result` to `AsyncResult` by applying an async function to a contained `Err` value, + * Maps a `Result` to `ResultAsync` by applying an async function to a contained `Err` value, * leaving an `Ok` value untouched. * * This function can be used to pass through a successful result while handling an error. * * @param f - The async function to apply to the contained error - * @returns A new AsyncResult with the async function applied to the contained error if `Err`, + * @returns A new ResultAsync with the async function applied to the contained error if `Err`, * or the original value if `Ok` * * @example @@ -499,11 +502,11 @@ export class ResultImpl { * assertEquals(x.mapErrAsync(stringify), Err("error code: 13")); * ``` */ - public mapErrAsync(f: (error: E) => Promise): AsyncResult { + public mapErrAsync(f: (error: E) => Promise): ResultAsync { if (this._ok) { - return new AsyncResult(Promise.resolve(Ok(this._value as T))); + return new ResultAsync(Promise.resolve(Ok(this._value as T))); } - return new AsyncResult(f(this._value as E).then((error) => Err(error))); + return new ResultAsync(f(this._value as E).then((error) => Err(error))); } /** @@ -545,11 +548,11 @@ export class ResultImpl { * .expect("failed to parse number"); * ``` */ - public inspectAsync(f: (value: T) => Promise): AsyncResult { + public inspectAsync(f: (value: T) => Promise): ResultAsync { if (this._ok) { - return new AsyncResult(f(this._value as T).then(() => this as unknown as Result)); + return new ResultAsync(f(this._value as T).then(() => this as unknown as Result)); } - return new AsyncResult(Promise.resolve(this as unknown as Result)); + return new ResultAsync(Promise.resolve(this as unknown as Result)); } /** @@ -768,12 +771,12 @@ export class ResultImpl { * ``` */ public andThenAsync( - f: (value: T) => AsyncResult | Promise>, - ): AsyncResult { + f: (value: T) => ResultAsync | Promise>, + ): ResultAsync { if (this._ok) { - return new AsyncResult(f(this._value as T)); + return new ResultAsync(f(this._value as T)); } - return new AsyncResult(Promise.resolve(Err(this._value as E))); + return new ResultAsync(Promise.resolve(Err(this._value as E))); } /** @@ -854,12 +857,12 @@ export class ResultImpl { * ``` */ public orElseAsync( - f: (error: E) => AsyncResult | Promise>, - ): AsyncResult { + f: (error: E) => ResultAsync | Promise>, + ): ResultAsync { if (this._ok) { - return new AsyncResult(Promise.resolve(this) as Promise>); + return new ResultAsync(Promise.resolve(this) as Promise>); } - return new AsyncResult(f(this._value as E)); + return new ResultAsync(f(this._value as E)); } /** @@ -1112,7 +1115,7 @@ export namespace Result { } /** - * Tries to resolve a promise and returns the result as a `AsyncResult`. + * Tries to resolve a promise and returns the result as a `ResultAsync`. * * This may allow a synchronous error to escape, prefer using `Result.fromThrowableAsync()` instead. * @@ -1131,12 +1134,12 @@ export namespace Result { * * @example * ```typescript - * // const result: AsyncResult + * // const result: ResultAsync * const result = Result.fromPromise(Promise.resolve(42)) * ``` */ - export function fromPromise(promise: Promise): AsyncResult { - return new AsyncResult( + export function fromPromise(promise: Promise): ResultAsync { + return new ResultAsync( promise.then( (value) => Ok(value), (error) => Err(handleCaughtError(error)), @@ -1145,7 +1148,7 @@ export namespace Result { } /** - * Tries to execute an async function and returns the result as a `AsyncResult`. + * Tries to execute an async function and returns the result as a `ResultAsync`. * * This is safer then `Result.fromPromise()` because it will not allow a synchronous error to escape. * @@ -1164,7 +1167,7 @@ export namespace Result { * * @example * ```typescript - * // const result: AsyncResult + * // const result: ResultAsync * const result = Result.fromThrowableAsync((): Promise => { * if (Math.random() > 0.5) { * throw new Error("random error") @@ -1173,7 +1176,7 @@ export namespace Result { * } * }) */ - export function fromThrowableAsync(f: () => Promise): AsyncResult { + export function fromThrowableAsync(f: () => Promise): ResultAsync { async function safe(): Promise> { try { return Ok(await f() as T); @@ -1181,8 +1184,6 @@ export namespace Result { return Err(handleCaughtError(error)); } } - return new AsyncResult(safe()); + return new ResultAsync(safe()); } } - -console.log(Ok(new Map([["a", 1]]))); diff --git a/src/async_result.ts b/src/result_async.ts similarity index 71% rename from src/async_result.ts rename to src/result_async.ts index 9f26203..d4157a2 100644 --- a/src/async_result.ts +++ b/src/result_async.ts @@ -1,13 +1,13 @@ /** - * This module contains the `AsyncResult` class, which is a promise that resolves to a `Result`. + * This module contains the `ResultAsync` class, which is a promise that resolves to a `Result`. * @module */ -import { AsyncOption } from "./async_option.ts"; +import { OptionAsync } from "./option_async.ts"; import { Err, Ok, type Result, type ResultMatch, type ResultMatchAsync } from "./result.ts"; import type { InferErr, InferOk } from "./util.ts"; -type WrappedResult = Promise> | PromiseLike> | AsyncResult; +type WrappedResult = Promise> | PromiseLike> | ResultAsync; /** * A promise that resolves to a `Result`. @@ -16,29 +16,29 @@ type WrappedResult = Promise> | PromiseLike> | A * * This class also implements the `PromiseLike>` interface, so it can be awaited like a `Promise` to convert to a `Result`. */ -export class AsyncResult implements PromiseLike> { +export class ResultAsync implements PromiseLike> { /** - * Takes an array of `AsyncResult`s and returns `Ok` when all of them are `Ok`, otherwise returns the first `Err`. + * Takes an array of `ResultAsync`s and returns `Ok` when all of them are `Ok`, otherwise returns the first `Err`. * * Uses `Promise.all()` under the hood. * - * @param results - The `AsyncResult`s to resolve - * @returns A new `AsyncResult` that resolves to an array of the results of the given `AsyncResult`s + * @param results - The `ResultAsync`s to resolve + * @returns A new `ResultAsync` that resolves to an array of the results of the given `ResultAsync`s * * @example * ```typescript - * const allOks = [AsyncOk(1), AsyncOk(2)]; - * const result = AsyncResult.all(allOks); + * const allOks = [OkAsync(1), OkAsync(2)]; + * const result = ResultAsync.all(allOks); * assertEquals(await result, Ok([1, 2])); * - * const withErr = [AsyncOk(1), AsyncErr("error")]; - * const result2 = AsyncResult.all(withErr); + * const withErr = [OkAsync(1), ErrAsync("error")]; + * const result2 = ResultAsync.all(withErr); * assertEquals(await result2, Err("error")); * ``` */ - public static all[] | []>( + public static all[] | []>( results: A, - ): AsyncResult< + ): ResultAsync< { -readonly [I in keyof A]: InferOk> }, InferErr> > { @@ -46,7 +46,7 @@ export class AsyncResult implements PromiseLike> { for (const result of results) { promises.push(result.toPromise()); } - return new AsyncResult( + return new ResultAsync( Promise.all(promises).then((values) => { return Ok(values as any); }).catch((e) => { @@ -56,21 +56,21 @@ export class AsyncResult implements PromiseLike> { } /** - * Takes an array of `AsyncResult`s and returns a `Promise` that resolves to an array of `Result`s. + * Takes an array of `ResultAsync`s and returns a `Promise` that resolves to an array of `Result`s. * * Uses `Promise.allSettled()` under the hood. * - * @param results - The `AsyncResult`s to resolve - * @returns A promise that resolves to an array of the results of the given `AsyncResult`s + * @param results - The `ResultAsync`s to resolve + * @returns A promise that resolves to an array of the results of the given `ResultAsync`s * * @example * ```typescript - * const allOks = [AsyncOk(1), AsyncErr("error"), AsyncOk(2)]; - * const result = AsyncResult.allSettled(allOks); + * const allOks = [OkAsync(1), ErrAsync("error"), OkAsync(2)]; + * const result = ResultAsync.allSettled(allOks); * assertEquals(await result, [Ok(1), Err("error"), Ok(2)]); * ``` */ - public static async allSettled[] | []>( + public static async allSettled[] | []>( results: A, ): Promise< { -readonly [I in keyof A]: Result>, InferErr>> } @@ -88,27 +88,27 @@ export class AsyncResult implements PromiseLike> { } /** - * Takes an array of `AsyncResult`s and returns the first `Ok`, otherwise returns an `Err` containing an array of all the errors. + * Takes an array of `ResultAsync`s and returns the first `Ok`, otherwise returns an `Err` containing an array of all the errors. * * Uses `Promise.any()` under the hood. * - * @param results - The `AsyncResult`s to resolve - * @returns A new `AsyncResult` that resolves to the first `Ok` result, or an `Err` containing all the errors if all results are `Err` + * @param results - The `ResultAsync`s to resolve + * @returns A new `ResultAsync` that resolves to the first `Ok` result, or an `Err` containing all the errors if all results are `Err` * * @example * ```typescript - * const anyOk = [AsyncOk(1), AsyncErr("error"), AsyncOk(2)]; - * const result = AsyncResult.any(anyOk); + * const anyOk = [OkAsync(1), ErrAsync("error"), OkAsync(2)]; + * const result = ResultAsync.any(anyOk); * assertEquals(await result, Ok(1)); * - * const allErrs = [AsyncErr("error1"), AsyncErr("error2")]; - * const result2 = AsyncResult.any(allErrs); + * const allErrs = [ErrAsync("error1"), ErrAsync("error2")]; + * const result2 = ResultAsync.any(allErrs); * assertEquals(await result2, Err(["error1", "error2"])); * ``` */ - public static any[] | []>( + public static any[] | []>( results: A, - ): AsyncResult< + ): ResultAsync< InferOk>, { -readonly [I in keyof A]: InferErr> } > { @@ -116,7 +116,7 @@ export class AsyncResult implements PromiseLike> { for (const result of results) { promises.push(result.toPromise()); } - return new AsyncResult( + return new ResultAsync( Promise.any(promises).then((value) => { return Ok(value); }).catch((e) => { @@ -128,16 +128,16 @@ export class AsyncResult implements PromiseLike> { } /** - * Takes an array of `AsyncResult`s and returns the first settled `Result`. + * Takes an array of `ResultAsync`s and returns the first settled `Result`. * * Uses `Promise.race()` under the hood. * - * @param results - The `AsyncResult`s to resolve - * @returns A new `AsyncResult` that resolves to the first settled `Result` + * @param results - The `ResultAsync`s to resolve + * @returns A new `ResultAsync` that resolves to the first settled `Result` * * @example * ```typescript - * const quick: AsyncResult = new AsyncResult( + * const quick: ResultAsync = new ResultAsync( * new Promise((resolve) => { * setTimeout( * resolve, @@ -146,7 +146,7 @@ export class AsyncResult implements PromiseLike> { * ); * }), * ); - * const slow: AsyncResult = new AsyncResult( + * const slow: ResultAsync = new ResultAsync( * new Promise((resolve) => { * setTimeout( * resolve, @@ -155,13 +155,13 @@ export class AsyncResult implements PromiseLike> { * ); * }), * ); - * const result = AsyncResult.race([quick, slow]); + * const result = ResultAsync.race([quick, slow]); * assertEquals(await result, Ok("quick")); * ``` */ - public static race[] | []>( + public static race[] | []>( results: A, - ): AsyncResult< + ): ResultAsync< InferOk>, InferErr> > { @@ -169,7 +169,7 @@ export class AsyncResult implements PromiseLike> { for (const result of results) { promises.push(result.toPromise()); } - return new AsyncResult( + return new ResultAsync( Promise.race(promises).then((value) => { return Ok(value); }).catch((e) => { @@ -184,37 +184,37 @@ export class AsyncResult implements PromiseLike> { this.promise = promise; } - public get [Symbol.toStringTag](): "AsyncResult" { - return "AsyncResult"; + public get [Symbol.toStringTag](): "ResultAsync" { + return "ResultAsync"; } public toJSON(): { - AsyncResult: WrappedResult; + ResultAsync: WrappedResult; } { - return { AsyncResult: this.promise }; + return { ResultAsync: this.promise }; } public toString(): string { - return `AsyncResult(${this.promise.toString()})`; + return `ResultAsync(${this.promise.toString()})`; } public [Symbol.for("nodejs.util.inspect.custom")](): { - AsyncResult: WrappedResult; + ResultAsync: WrappedResult; } { return this.toJSON(); } /** - * Converts an `AsyncResult` to a regular `Promise`. + * Converts an `ResultAsync` to a regular `Promise`. * * @returns A promise that resolves to the contained value if `Ok`, otherwise throws the contained error * * @example * ```typescript - * const x: AsyncResult = AsyncOk(2) + * const x: ResultAsync = OkAsync(2) * assertEquals(await x.toPromise(), 2) * - * const y: AsyncResult = AsyncErr("error") + * const y: ResultAsync = ErrAsync("error") * await y.toPromise().catch((e) => assertEquals(e, "error")) * ``` */ @@ -265,13 +265,13 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * const x: AsyncResult = AsyncOk(2) + * const x: ResultAsync = OkAsync(2) * assertEquals(await x.match({ * Ok: (v) => v * 2, * Err: (e) => e.length, * }), 4) * - * const y: AsyncResult = AsyncErr("error") + * const y: ResultAsync = ErrAsync("error") * assertEquals(await y.match({ * Ok: (v) => v * 2, * Err: (e) => e.length, @@ -290,13 +290,13 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * const x: AsyncResult = AsyncOk(2) + * const x: ResultAsync = OkAsync(2) * assertEquals(await x.matchAsync({ * Ok: async (v) => v * 2, * Err: async (e) => e.length, * }), 4) * - * const y: AsyncResult = AsyncErr("error") + * const y: ResultAsync = ErrAsync("error") * assertEquals(await y.matchAsync({ * Ok: async (v) => v * 2, * Err: async (e) => e.length, @@ -308,39 +308,39 @@ export class AsyncResult implements PromiseLike> { } /** - * Converts from `AsyncResult` to `AsyncOption`, discarding the error if any. + * Converts from `ResultAsync` to `OptionAsync`, discarding the error if any. * * @returns An `Option` containing the success value if this is `Ok`, or `None` if this is `Err` * * @example * ```typescript - * const x: AsyncResult = AsyncOk(2); + * const x: ResultAsync = OkAsync(2); * assertEquals(await x.ok(), Some(2)); * - * const x: AsyncResult = AsyncErr("Nothing here"); + * const x: ResultAsync = ErrAsync("Nothing here"); * assertEquals(await x.ok(), None); * ``` */ - public ok(): AsyncOption { - return new AsyncOption(this.then((result) => result.ok())); + public ok(): OptionAsync { + return new OptionAsync(this.then((result) => result.ok())); } /** - * Converts from `AsyncResult` to `AsyncOption`, discarding the success value if any. + * Converts from `ResultAsync` to `OptionAsync`, discarding the success value if any. * - * @returns An `AsyncOption` containing the error value if this is `Err`, or `None` if this is `Ok`. + * @returns An `OptionAsync` containing the error value if this is `Err`, or `None` if this is `Ok`. * * @example * ```typescript - * const x: AsyncResult = AsyncOk(2); + * const x: ResultAsync = OkAsync(2); * assertEquals(await x.err(), None); * - * const x: AsyncResult = AsyncErr("Nothing here"); + * const x: ResultAsync = ErrAsync("Nothing here"); * assertEquals(await x.err(), Some("Nothing here")); * ``` */ - public err(): AsyncOption { - return new AsyncOption(this.then((result) => result.err())); + public err(): OptionAsync { + return new OptionAsync(this.then((result) => result.err())); } /** @@ -351,25 +351,25 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * let x: AsyncResult = AsyncOk(2) - * let y: AsyncResult = AsyncErr("late error") + * let x: ResultAsync = OkAsync(2) + * let y: ResultAsync = ErrAsync("late error") * assertEquals(await x.and(y), Err("late error")) * - * let x: AsyncResult = AsyncErr("early error") - * let y: AsyncResult = AsyncOk("foo") + * let x: ResultAsync = ErrAsync("early error") + * let y: ResultAsync = OkAsync("foo") * assertEquals(await x.and(y), Err("early error")) * - * let x: AsyncResult = AsyncErr("not a 2") - * let y: AsyncResult = AsyncErr("late error") + * let x: ResultAsync = ErrAsync("not a 2") + * let y: ResultAsync = ErrAsync("late error") * assertEquals(await x.and(y), Err("not a 2")) * - * let x: AsyncResult = AsyncOk(2) - * let y: AsyncResult = AsyncOk("different result type") + * let x: ResultAsync = OkAsync(2) + * let y: ResultAsync = OkAsync("different result type") * assertEquals(await x.and(y), Ok("different result type")) * ``` */ - public and(other: AsyncResult): AsyncResult { - return new AsyncResult( + public and(other: ResultAsync): ResultAsync { + return new ResultAsync( this.then((result) => other.then((otherResult) => result.and(otherResult))), ); } @@ -391,9 +391,9 @@ export class AsyncResult implements PromiseLike> { * return Ok((a / b).toString()) * } * - * assertEquals(await AsyncOk(100).andThen(divideThenToString), Ok("50")) - * assertEquals(await AsyncOk(100).andThen(divideThenToString), Err("division by zero")) - * assertEquals(await AsyncErr("not a number").andThen(divideThenToString), Err("not a number")) + * assertEquals(await OkAsync(100).andThen(divideThenToString), Ok("50")) + * assertEquals(await OkAsync(100).andThen(divideThenToString), Err("division by zero")) + * assertEquals(await ErrAsync("not a number").andThen(divideThenToString), Err("not a number")) * * // Often used to chain fallible operations that may return Err * const json = await Result.fromThrowableAsync(async () => { @@ -411,8 +411,8 @@ export class AsyncResult implements PromiseLike> { * assertEquals(shouldFail.isErr(), true) * ``` */ - public andThen(f: (value: T) => Result): AsyncResult { - return new AsyncResult(this.then((result) => result.andThen((value) => f(value)))); + public andThen(f: (value: T) => Result): ResultAsync { + return new ResultAsync(this.then((result) => result.andThen((value) => f(value)))); } /** @@ -432,9 +432,9 @@ export class AsyncResult implements PromiseLike> { * return Ok((a / b).toString()) * } * - * assertEquals(await AsyncOk(100, 2).andThenAsync(divideThenToString), Ok("50")) - * assertEquals(await AsyncOk(100, 0).andThenAsync(divideThenToString), Err("division by zero")) - * assertEquals(await AsyncErr("not a number").andThenAsync(divideThenToString), Err("not a number")) + * assertEquals(await OkAsync(100, 2).andThenAsync(divideThenToString), Ok("50")) + * assertEquals(await OkAsync(100, 0).andThenAsync(divideThenToString), Err("division by zero")) + * assertEquals(await ErrAsync("not a number").andThenAsync(divideThenToString), Err("not a number")) * * // Often used to chain fallible operations that may return Err * const json = await Result.fromThrowableAsync(async () => { @@ -452,8 +452,8 @@ export class AsyncResult implements PromiseLike> { * assertEquals(shouldFail.isErr(), true) * ``` */ - public andThenAsync(f: (value: T) => Promise>): AsyncResult { - return new AsyncResult(this.then((result) => result.andThenAsync((value) => f(value)))); + public andThenAsync(f: (value: T) => Promise>): ResultAsync { + return new ResultAsync(this.then((result) => result.andThenAsync((value) => f(value)))); } /** @@ -469,7 +469,7 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * const x: AsyncResult = AsyncErr("emergency failure"); + * const x: ResultAsync = ErrAsync("emergency failure"); * await x.expect("Testing expect"); // throws Panic: Testing expect: emergency failure * ``` * @@ -499,7 +499,7 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * const x: AsyncResult = AsyncOk(10); + * const x: ResultAsync = OkAsync(10); * await x.expectErr("Testing expectErr"); // throws Panic: Testing expectErr: 10 * ``` */ @@ -508,32 +508,32 @@ export class AsyncResult implements PromiseLike> { } /** - * Converts from `AsyncResult, E>` to `AsyncResult`. + * Converts from `ResultAsync, E>` to `ResultAsync`. * - * @returns A flattened `AsyncResult`. + * @returns A flattened `ResultAsync`. * * @example * ```typescript * // Basic usage: - * let x: AsyncResult, number> = AsyncOk(Ok("hello")); + * let x: ResultAsync, number> = OkAsync(Ok("hello")); * assertEquals(await x.flatten(), Ok("hello")); * - * let x: AsyncResult, number> = AsyncOk(Err(6)); + * let x: ResultAsync, number> = OkAsync(Err(6)); * assertEquals(await x.flatten(), Err(6)); * - * let x: AsyncResult, number> = AsyncErr(6); + * let x: ResultAsync, number> = ErrAsync(6); * assertEquals(await x.flatten(), Err(6)); * * // Flattening only removes one level of nesting at a time: - * let x: AsyncResult, number>, number> = AsyncOk(Ok(Ok("hello"))); + * let x: ResultAsync, number>, number> = OkAsync(Ok(Ok("hello"))); * assertEquals(await x.flatten(), Ok(Ok("hello"))); * assertEquals(await x.flatten().flatten(), Ok("hello")); * ``` */ public flatten>( - this: AsyncResult, - ): AsyncResult, InferErr | E> { - return new AsyncResult(this.then((result) => result.flatten())); + this: ResultAsync, + ): ResultAsync, InferErr | E> { + return new ResultAsync(this.then((result) => result.flatten())); } /** @@ -552,8 +552,8 @@ export class AsyncResult implements PromiseLike> { * .expect("failed to parse number"); * ``` */ - public inspect(f: (value: T) => void): AsyncResult { - return new AsyncResult(this.then((result) => result.inspect(f))); + public inspect(f: (value: T) => void): ResultAsync { + return new ResultAsync(this.then((result) => result.inspect(f))); } /** @@ -572,8 +572,8 @@ export class AsyncResult implements PromiseLike> { * .expect("failed to parse number"); * ``` */ - public inspectAsync(f: (value: T) => Promise): AsyncResult { - return new AsyncResult(this.then((result) => result.inspectAsync(f))); + public inspectAsync(f: (value: T) => Promise): ResultAsync { + return new ResultAsync(this.then((result) => result.inspectAsync(f))); } /** @@ -592,8 +592,8 @@ export class AsyncResult implements PromiseLike> { * .map(async (contents) => processContents(contents)); * ``` */ - public inspectErr(f: (error: E) => void): AsyncResult { - return new AsyncResult(this.then((result) => result.inspectErr(f))); + public inspectErr(f: (error: E) => void): ResultAsync { + return new ResultAsync(this.then((result) => result.inspectErr(f))); } /** @@ -611,26 +611,26 @@ export class AsyncResult implements PromiseLike> { * .map((contents) => processContents(contents)); * ``` */ - public inspectErrAsync(f: (error: E) => Promise): AsyncResult { - return new AsyncResult(this.then((result) => result.inspectErrAsync(f))); + public inspectErrAsync(f: (error: E) => Promise): ResultAsync { + return new ResultAsync(this.then((result) => result.inspectErrAsync(f))); } /** - * Maps a `AsyncResult` to `AsyncResult` by applying a function to a contained `Ok` value, + * Maps a `ResultAsync` to `ResultAsync` by applying a function to a contained `Ok` value, * leaving an `Err` value untouched. * * This function can be used to compose the results of two functions. * * @param f - The function to apply to the contained value - * @returns A new AsyncResult with the function applied to the contained value if `Ok`, + * @returns A new ResultAsync with the function applied to the contained value if `Ok`, * or the original error if `Err` * * @example * ```typescript - * const x = await AsyncOk(2).map((x) => x.toString()); + * const x = await OkAsync(2).map((x) => x.toString()); * assertEquals(x, Ok("2")); * - * const x = await AsyncErr("error").map((x) => x.toString()); + * const x = await ErrAsync("error").map((x) => x.toString()); * assertEquals(x, Err("error")); * * // Processing lines of numbers: @@ -646,26 +646,26 @@ export class AsyncResult implements PromiseLike> { * } * ``` */ - public map(f: (value: T) => U): AsyncResult { - return new AsyncResult(this.then((result) => result.map(f))); + public map(f: (value: T) => U): ResultAsync { + return new ResultAsync(this.then((result) => result.map(f))); } /** - * Maps a `AsyncResult` to `AsyncResult` by applying an async function to a contained `Ok` value, + * Maps a `ResultAsync` to `ResultAsync` by applying an async function to a contained `Ok` value, * leaving an `Err` value untouched. * * This function can be used to compose the results of two functions. * * @param f - The async function to apply to the contained value - * @returns A new AsyncResult with the async function applied to the contained value if `Ok`, + * @returns A new ResultAsync with the async function applied to the contained value if `Ok`, * or the original error if `Err` * * @example * ```typescript - * const x = await AsyncOk(2).mapAsync(async (x) => x.toString()); + * const x = await OkAsync(2).mapAsync(async (x) => x.toString()); * assertEquals(x, Ok("2")); * - * const x = await AsyncErr("error").mapAsync(async (x) => x.toString()); + * const x = await ErrAsync("error").mapAsync(async (x) => x.toString()); * assertEquals(x, Err("error")); * * // Processing lines of numbers: @@ -681,12 +681,12 @@ export class AsyncResult implements PromiseLike> { * } * ``` */ - public mapAsync(f: (value: T) => Promise): AsyncResult { - return new AsyncResult(this.then((result) => result.mapAsync(f))); + public mapAsync(f: (value: T) => Promise): ResultAsync { + return new ResultAsync(this.then((result) => result.mapAsync(f))); } /** - * Maps a `AsyncResult` to `AsyncResult` by applying a function to a contained `Err` value, + * Maps a `ResultAsync` to `ResultAsync` by applying a function to a contained `Err` value, * leaving an `Ok` value untouched. * * This function can be used to pass through a successful result while handling an error. @@ -699,40 +699,40 @@ export class AsyncResult implements PromiseLike> { * ```typescript * const stringify = (x: number): string => `error code: ${x}`; * - * const x: AsyncResult = AsyncOk(2); + * const x: ResultAsync = OkAsync(2); * assertEquals(await x.mapErr(stringify), Ok(2)); * - * const x: AsyncResult = AsyncErr(13); + * const x: ResultAsync = ErrAsync(13); * assertEquals(await x.mapErr(stringify), Err("error code: 13")); * ``` */ - public mapErr(f: (error: E) => F): AsyncResult { - return new AsyncResult(this.then((result) => result.mapErr(f))); + public mapErr(f: (error: E) => F): ResultAsync { + return new ResultAsync(this.then((result) => result.mapErr(f))); } /** - * Maps a `AsyncResult` to `AsyncResult` by applying an async function to a contained `Err` value, + * Maps a `ResultAsync` to `ResultAsync` by applying an async function to a contained `Err` value, * leaving an `Ok` value untouched. * * This function can be used to pass through a successful result while handling an error. * * @param f - The async function to apply to the contained error - * @returns A new AsyncResult with the async function applied to the contained error if `Err`, + * @returns A new ResultAsync with the async function applied to the contained error if `Err`, * or the original value if `Ok` * * @example * ```typescript * const stringify = async (x: number): Promise => `error code: ${x}`; * - * const x: AsyncResult = AsyncOk(2); + * const x: ResultAsync = OkAsync(2); * assertEquals(await x.mapErrAsync(stringify), Ok(2)); * - * const x: AsyncResult = AsyncErr(13); + * const x: ResultAsync = ErrAsync(13); * assertEquals(await x.mapErrAsync(stringify), Err("error code: 13")); * ``` */ - public mapErrAsync(f: (error: E) => Promise): AsyncResult { - return new AsyncResult(this.then((result) => result.mapErrAsync(f))); + public mapErrAsync(f: (error: E) => Promise): ResultAsync { + return new ResultAsync(this.then((result) => result.mapErrAsync(f))); } /** @@ -744,10 +744,10 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * let x: AsyncResult = AsyncOk("foo") + * let x: ResultAsync = OkAsync("foo") * assertEquals(await x.mapOr(42, (v) => v.length), 3) * - * let x: AsyncResult = AsyncErr("bar") + * let x: ResultAsync = ErrAsync("bar") * assertEquals(await x.mapOr(42, (v) => v.length), 42) * ``` */ @@ -764,10 +764,10 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * let x: AsyncResult = AsyncOk("foo") + * let x: ResultAsync = OkAsync("foo") * assertEquals(await x.mapOrAsync(42, async (v) => v.length), 3) * - * let x: AsyncResult = AsyncErr("bar") + * let x: ResultAsync = ErrAsync("bar") * assertEquals(await x.mapOrAsync(42, async (v) => v.length), 42) * ``` */ @@ -776,7 +776,7 @@ export class AsyncResult implements PromiseLike> { } /** - * Maps a `AsyncResult` to `A | B` by applying fallback function `defaultValue` to a contained `Err` value, + * Maps a `ResultAsync` to `A | B` by applying fallback function `defaultValue` to a contained `Err` value, * or function `f` to a contained `Ok` value. * * This function can be used to unpack a successful result while handling an error. @@ -789,7 +789,7 @@ export class AsyncResult implements PromiseLike> { * ```typescript * const k = 21; * - * let x: AsyncResult = AsyncOk("foo"); + * let x: ResultAsync = OkAsync("foo"); * assertEquals(await x.mapOrElse((e) => k * 2, (v) => v.length), 3); * * let x: Result = Err("bar"); @@ -804,7 +804,7 @@ export class AsyncResult implements PromiseLike> { } /** - * Maps a `AsyncResult` to `Promise` by applying fallback function `defaultValue` to a contained `Err` value, + * Maps a `ResultAsync` to `Promise` by applying fallback function `defaultValue` to a contained `Err` value, * or function `f` to a contained `Ok` value. * * This function can be used to unpack a successful result while handling an error. @@ -817,10 +817,10 @@ export class AsyncResult implements PromiseLike> { * ```typescript * const k = 21; * - * let x: AsyncResult = AsyncOk("foo"); + * let x: ResultAsync = OkAsync("foo"); * assertEquals(await x.mapOrElseAsync(async (e) => k * 2, (v) => v.length), 3); * - * let x: AsyncResult = AsyncErr("bar"); + * let x: ResultAsync = ErrAsync("bar"); * assertEquals(await x.mapOrElseAsync(async (e) => k * 2, async (v) => v.length), 42); * ``` */ @@ -839,25 +839,25 @@ export class AsyncResult implements PromiseLike> { * * @example * ```typescript - * let x: AsyncResult = AsyncOk(2) - * let y: AsyncResult = AsyncErr("late error") + * let x: ResultAsync = OkAsync(2) + * let y: ResultAsync = ErrAsync("late error") * assertEquals(await x.or(y), Ok(2)) * - * let x: AsyncResult = AsyncErr("early error") - * let y: AsyncResult = AsyncOk(2) + * let x: ResultAsync = ErrAsync("early error") + * let y: ResultAsync = OkAsync(2) * assertEquals(await x.or(y), Ok(2)) * - * let x: AsyncResult = AsyncErr("not a 2") - * let y: AsyncResult = AsyncErr("late error") + * let x: ResultAsync = ErrAsync("not a 2") + * let y: ResultAsync = ErrAsync("late error") * assertEquals(await x.or(y), Err("late error")) * - * let x: AsyncResult = AsyncOk(2) - * let y: AsyncResult = AsyncOk(100) + * let x: ResultAsync = OkAsync(2) + * let y: ResultAsync = OkAsync(100) * assertEquals(await x.or(y), Ok(2)) * ``` */ - public or(other: AsyncResult): AsyncResult { - return new AsyncResult( + public or(other: ResultAsync): ResultAsync { + return new ResultAsync( this.then((thisResult) => other.then((otherResult) => thisResult.or(otherResult))), ); } @@ -875,14 +875,14 @@ export class AsyncResult implements PromiseLike> { * function sq(x: number): Result { return Ok(x * x) } * function err(x: number): Result { return Err(x) } * - * assertEquals(await AsyncOk(2).orElse(sq).orElse(sq), Ok(2)) - * assertEquals(await AsyncOk(2).orElse(err).orElse(sq), Ok(2)) - * assertEquals(await AsyncErr(3).orElse(sq).orElse(err), Ok(9)) - * assertEquals(await AsyncErr(3).orElse(err).orElse(err), Err(3)) + * assertEquals(await OkAsync(2).orElse(sq).orElse(sq), Ok(2)) + * assertEquals(await OkAsync(2).orElse(err).orElse(sq), Ok(2)) + * assertEquals(await ErrAsync(3).orElse(sq).orElse(err), Ok(9)) + * assertEquals(await ErrAsync(3).orElse(err).orElse(err), Err(3)) * ``` */ - public orElse(f: (error: E) => Result): AsyncResult { - return new AsyncResult(this.then((thisResult) => thisResult.orElse((error) => f(error)))); + public orElse(f: (error: E) => Result): ResultAsync { + return new ResultAsync(this.then((thisResult) => thisResult.orElse((error) => f(error)))); } /** @@ -898,14 +898,14 @@ export class AsyncResult implements PromiseLike> { * async function sq(x: number): Promise> { return Ok(x * x) } * async function err(x: number): Promise> { return Err(x) } * - * assertEquals(await AsyncOk(2).orElseAsync(sq).orElseAsync(sq), Ok(2)) - * assertEquals(await AsyncOk(2).orElseAsync(err).orElseAsync(sq), Ok(2)) - * assertEquals(await AsyncErr(3).orElseAsync(sq).orElseAsync(err), Ok(9)) - * assertEquals(await AsyncErr(3).orElseAsync(err).orElseAsync(err), Err(3)) + * assertEquals(await OkAsync(2).orElseAsync(sq).orElseAsync(sq), Ok(2)) + * assertEquals(await OkAsync(2).orElseAsync(err).orElseAsync(sq), Ok(2)) + * assertEquals(await ErrAsync(3).orElseAsync(sq).orElseAsync(err), Ok(9)) + * assertEquals(await ErrAsync(3).orElseAsync(err).orElseAsync(err), Err(3)) * ``` */ - public orElseAsync(f: (error: E) => Promise>): AsyncResult { - return new AsyncResult( + public orElseAsync(f: (error: E) => Promise>): ResultAsync { + return new ResultAsync( this.then((thisResult) => thisResult.orElseAsync((error) => f(error))), ); } @@ -920,10 +920,10 @@ export class AsyncResult implements PromiseLike> { * ```typescript * const defaultValue = 2 * - * let x: AsyncResult = AsyncOk(9) + * let x: ResultAsync = OkAsync(9) * assertEquals(await x.unwrapOr(defaultValue), 9) * - * let x: AsyncResult = AsyncErr("error") + * let x: ResultAsync = ErrAsync("error") * assertEquals(await x.unwrapOr(defaultValue), defaultValue) * ``` */ @@ -941,8 +941,8 @@ export class AsyncResult implements PromiseLike> { * ```typescript * function count(x: string): number { return x.length } * - * assertEquals(await AsyncOk(2).unwrapOrElse(count), 2) - * assertEquals(await AsyncErr("foo").unwrapOrElse(count), 3) + * assertEquals(await OkAsync(2).unwrapOrElse(count), 2) + * assertEquals(await ErrAsync("foo").unwrapOrElse(count), 3) * ``` */ public async unwrapOrElse(defaultValue: (error: E) => U): Promise { @@ -959,11 +959,19 @@ export class AsyncResult implements PromiseLike> { * ```typescript * async function count(x: string): Promise { return x.length } * - * assertEquals(await AsyncOk(2).unwrapOrElseAsync(count), 2) - * assertEquals(await AsyncErr("foo").unwrapOrElseAsync(count), 3) + * assertEquals(await OkAsync(2).unwrapOrElseAsync(count), 2) + * assertEquals(await ErrAsync("foo").unwrapOrElseAsync(count), 3) * ``` */ public async unwrapOrElseAsync(defaultValue: (error: E) => Promise): Promise { return (await this).unwrapOrElseAsync(defaultValue); } } + +export function OkAsync(value: T): ResultAsync { + return new ResultAsync(Promise.resolve(Ok(value))); +} + +export function ErrAsync(error: E): ResultAsync { + return new ResultAsync(Promise.resolve(Err(error))); +} diff --git a/src/run.ts b/src/run.ts index 46c678e..cfa2d16 100644 --- a/src/run.ts +++ b/src/run.ts @@ -1,13 +1,11 @@ -import { AsyncResult } from "./async_result.ts"; +import { ResultAsync } from "./result_async.ts"; import type { Result } from "./result.ts"; import type { InferErr, InferOk } from "./util.ts"; /** * Runs an async function and returns a `Result`. * - * This is useful for running async functions that return `Promise>` or `AsyncResult`. - * - * Alternatively you can use `asyncFn` to wrap the function or `tryBlockAsync` to wrap the function in a try/catch block. + * This is useful for running async functions that return `Promise>` or `ResultAsync`. * * @param fn - The async function to run. * @returns A `Result` that is `Ok(value)` if the async function resolves to an `Ok` variant, otherwise `Err(error)`. @@ -23,8 +21,8 @@ import type { InferErr, InferOk } from "./util.ts"; * assertEquals(result.isErr(), true); * ``` */ -export function runAsync> | AsyncResult>( +export function runAsync> | ResultAsync>( fn: () => R, -): AsyncResult>, InferErr>> { - return new AsyncResult(fn()); +): ResultAsync>, InferErr>> { + return new ResultAsync(fn()); } diff --git a/src/symbols.ts b/src/symbols.ts deleted file mode 100644 index 08667f3..0000000 --- a/src/symbols.ts +++ /dev/null @@ -1 +0,0 @@ -export const tag = Symbol("tag"); diff --git a/src/try.ts b/src/try.ts index 2374417..fe855bf 100644 --- a/src/try.ts +++ b/src/try.ts @@ -3,7 +3,7 @@ * @module */ -import { AsyncResult } from "./async_result.ts"; +import { ResultAsync } from "./result_async.ts"; import { Panic } from "./error.ts"; import type { Err, Result } from "./result.ts"; import type { InferErr, InferOk } from "./util.ts"; @@ -47,16 +47,16 @@ export function tryBlock, R extends Result>( } /** - * Creates an async scope where you can use `yield*` to unwrap or propagate errors from a `Result` or `AsyncResult`. + * Creates an async scope where you can use `yield*` to unwrap or propagate errors from a `Result` or `ResultAsync`. * * This is intended to emulate Rust's `try_blocks` and `?` operator and offer a more ergonomic way to handle errors, * just be aware that this can be significantly slower than manually handling and propagating errors because of the generator overhead. * - * The `scope` function should not throw any errors, instead it should return a `Result` or `AsyncResult` that contains + * The `scope` function should not throw any errors, instead it should return a `Result` or `ResultAsync` that contains * the error. If an error is thrown, it will be wrapped in a `Panic`. * - * @param scope - A generator function that yields `Result` or `AsyncResult` values - * @returns A `Result` or `AsyncResult` that is the value of the last `yield*` statement in the generator function + * @param scope - A generator function that yields `Result` or `ResultAsync` values + * @returns A `Result` or `ResultAsync` that is the value of the last `yield*` statement in the generator function * * @example * ```typescript @@ -83,9 +83,9 @@ export function tryBlock, R extends Result>( */ export function tryBlockAsync, R extends Result>( scope: () => AsyncGenerator, -): AsyncResult, InferErr | InferErr> { +): ResultAsync, InferErr | InferErr> { const next = scope().next(); - return new AsyncResult( + return new ResultAsync( next .then((result) => result.value as Result, InferErr | InferErr>) .catch((error) => { diff --git a/src/unwind.ts b/src/unwind.ts index 9e46dcb..4036e55 100644 --- a/src/unwind.ts +++ b/src/unwind.ts @@ -5,7 +5,7 @@ */ import { Err, Ok, Result } from "./result.ts"; -import { AsyncResult } from "./async_result.ts"; +import { ResultAsync } from "./result_async.ts"; import { Panic, parseError } from "./error.ts"; function unknownToError(e: unknown): Error { @@ -50,14 +50,14 @@ export function catchUnwind(fn: () => T): Result { } /** - * Wraps a function in an `AsyncResult` and catches any thrown error, even `Panics`, emulating Rust's `catch_unwind`. + * Wraps a function in an `ResultAsync` and catches any thrown error, even `Panics`, emulating Rust's `catch_unwind`. * * Panics are converted to Errors and preserved in the `cause` property * * This is useful for catching anything that can be thrown, including `Panics`, because `Result` helper methods deliberately let panics pass through. * * @param fn - The function to wrap. - * @returns An `AsyncResult` containing the return value of the function, or an `Error` if the function throws. + * @returns An `ResultAsync` containing the return value of the function, or an `Error` if the function throws. * * @example * ```ts @@ -66,7 +66,7 @@ export function catchUnwind(fn: () => T): Result { * }) * ``` */ -export function catchUnwindAsync(fn: () => Promise): AsyncResult { +export function catchUnwindAsync(fn: () => Promise): ResultAsync { async function unwind(): Promise> { try { return await Result.fromThrowableAsync(fn); // `await` is required here, otherwise the error is not caught @@ -74,5 +74,5 @@ export function catchUnwindAsync(fn: () => Promise): AsyncResult return Err(unknownToError(e)); } } - return new AsyncResult(unwind()); + return new ResultAsync(unwind()); } diff --git a/src/util.ts b/src/util.ts index 541f656..0ec1a55 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,8 +5,8 @@ import { type Err, type Ok, type Result, ResultImpl } from "./result.ts"; import { type Option, OptionImpl, type Some } from "./option.ts"; -import { AsyncResult } from "./async_result.ts"; -import { AsyncOption } from "./async_option.ts"; +import { ResultAsync } from "./result_async.ts"; +import { OptionAsync } from "./option_async.ts"; /** * Infers the `Ok` type from a `Result`. @@ -31,10 +31,10 @@ export function isResult(value: unknown): value is Result { } /** - * Checks if a value is an `AsyncResult`. + * Checks if a value is an `ResultAsync`. */ -export function isAsyncResult(value: unknown): value is AsyncResult { - return value instanceof AsyncResult; +export function isResultAsync(value: unknown): value is ResultAsync { + return value instanceof ResultAsync; } /** @@ -45,8 +45,8 @@ export function isOption(value: unknown): value is Option { } /** - * Checks if a value is an `AsyncOption`. + * Checks if a value is an `OptionAsync`. */ -export function isAsyncOption(value: unknown): value is AsyncOption { - return value instanceof AsyncOption; +export function isOptionAsync(value: unknown): value is OptionAsync { + return value instanceof OptionAsync; } diff --git a/tests/fn.test.ts b/tests/fn.test.ts deleted file mode 100644 index b0efe49..0000000 --- a/tests/fn.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -// deno-lint-ignore-file require-await -import { describe, it } from "@std/testing/bdd"; -import { expect } from "@std/expect"; -import { expectTypeOf } from "expect-type"; -import { asyncFn } from "../src/fn.ts"; -import { Err, Ok, Result } from "../src/result.ts"; -import { AsyncResult } from "../src/async_result.ts"; -import { ErrorWithTag } from "../src/error.ts"; - -export class TaggedError extends ErrorWithTag { - readonly tag = "TaggedError"; -} - -describe("asyncFn", () => { - it("returns Ok result when provided async function does not throw", async () => { - const wrappedFn = asyncFn(async () => Promise.resolve(Ok(42))); - const result = await wrappedFn(); - expect(result.expect("ok")).toEqual(42); - }); - - it("returns Err result when provided async function returns Err", async () => { - const wrappedFn = asyncFn(async () => Promise.resolve(Err("rekt"))); - const result = await wrappedFn(); - expect(result.expectErr("err")).toEqual("rekt"); - }); - - describe("types", () => { - it("returns correct type with function returning Promise", () => { - const f = async (_arg: number) => { - if (Math.random() > 0.5) { - return Ok(1); - } - if (Math.random() > 0.5) { - return Ok("foo"); - } - if (Math.random() > 0.5) { - return Err(1); - } - return Err("error"); - }; - const wrapped = asyncFn(f); - expectTypeOf(wrapped).parameter(0).toBeNumber(); - expectTypeOf(wrapped).returns.toEqualTypeOf< - AsyncResult - >(); - }); - - it("returns correct type with function returning Promise", () => { - const f = async (_arg: number) => Ok(1); - const wrapped = asyncFn(f); - expectTypeOf(wrapped).parameter(0).toBeNumber(); - expectTypeOf(wrapped).returns.toEqualTypeOf>(); - }); - - it("returns correct type with function returning Promise", () => { - const f = async (_arg: number) => Err(1); - const wrapped = asyncFn(f); - expectTypeOf(wrapped).parameter(0).toBeNumber(); - expectTypeOf(wrapped).returns.toEqualTypeOf>(); - }); - - it("returns correct type with function returning AsyncResult", () => { - const f = (_arg: number) => Result.fromPromise(Promise.resolve(1)); - const wrapped = asyncFn(f); - expectTypeOf(wrapped).parameter(0).toBeNumber(); - expectTypeOf(wrapped).returns.toEqualTypeOf>(); - }); - - it("returns correct type with function returning Promise", () => { - const f = async (_arg: number) => { - const bar = Result.fromPromise(Promise.resolve(1)); - return bar; - }; - const wrapped = asyncFn(f); - expectTypeOf(wrapped).parameter(0).toBeNumber(); - expectTypeOf(wrapped).returns.toEqualTypeOf>(); - }); - - it("works with generics", () => { - const wrapped = asyncFn(async (a: A, b: B) => { - if (Math.random() > 0.5) { - return Ok(a); - } - return Err(b); - }); - expectTypeOf(wrapped).branded.toEqualTypeOf< - (a: A, b: B) => AsyncResult - >(); - }); - - it("works with short-circuit return", () => { - const foo = asyncFn(async () => { - if (Math.random() > 0.5) { - return Ok(42); - } - return Err("error"); - }); - const wrapped = asyncFn(async () => { - const r = await foo(); - if (r.isErr()) { - return r; - } - return Ok(true); - }); - expectTypeOf(wrapped).returns.toEqualTypeOf>(); - }); - }); -}); diff --git a/tests/async_option.test.ts b/tests/option_async.test.ts similarity index 98% rename from tests/async_option.test.ts rename to tests/option_async.test.ts index 0700741..abad4ad 100644 --- a/tests/async_option.test.ts +++ b/tests/option_async.test.ts @@ -3,15 +3,15 @@ import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { assertSpyCall, assertSpyCalls, spy } from "@std/testing/mock"; import { None, Some } from "../src/option.ts"; -import { AsyncOption } from "../src/async_option.ts"; +import { OptionAsync } from "../src/option_async.ts"; import { Panic } from "../src/error.ts"; function promiseSome(value: T) { - return new AsyncOption(Promise.resolve(Some(value))); + return new OptionAsync(Promise.resolve(Some(value))); } function promiseNone() { - return new AsyncOption(Promise.resolve(None)); + return new OptionAsync(Promise.resolve(None)); } describe("okOr", () => { diff --git a/tests/async_result.test.ts b/tests/result_async.test.ts similarity index 90% rename from tests/async_result.test.ts rename to tests/result_async.test.ts index dfe2a63..b1203c3 100644 --- a/tests/async_result.test.ts +++ b/tests/result_async.test.ts @@ -3,18 +3,17 @@ import { describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { expectTypeOf } from "expect-type"; import { assertSpyCall, assertSpyCalls, spy } from "@std/testing/mock"; -import { AsyncResult } from "../src/async_result.ts"; +import { ErrAsync, OkAsync, ResultAsync } from "../src/result_async.ts"; import { Err, Ok, Result } from "../src/result.ts"; import { None, Some } from "../src/option.ts"; import { ErrorWithTag, Panic } from "../src/error.ts"; -import { AsyncErr, AsyncOk } from "../src/async_helpers.ts"; function TestOkPromise(value: T) { - return new AsyncResult(Promise.resolve(Ok(value))); + return new ResultAsync(Promise.resolve(Ok(value))); } function TestErrPromise(error: E) { - return new AsyncResult(Promise.resolve(Err(error))); + return new ResultAsync(Promise.resolve(Err(error))); } function TestOk(value: T): Result { @@ -127,20 +126,20 @@ describe("flatten", () => { it("works with an Ok result", async () => { const inner = TestOk(42); const flattened = TestOkPromise, boolean>(inner).flatten(); - expectTypeOf(flattened).toEqualTypeOf>(); + expectTypeOf(flattened).toEqualTypeOf>(); await expect(flattened.expect("ok")).resolves.toEqual(inner.expect("ok")); }); it("works with an Ok result", async () => { const inner = TestErr("error"); const flattened = TestOkPromise, boolean>(inner).flatten(); - expectTypeOf(flattened).toEqualTypeOf>(); + expectTypeOf(flattened).toEqualTypeOf>(); await expect(flattened.expectErr("err")).resolves.toEqual(inner.expectErr("err")); }); it("works with an Err result", async () => { const flattened = TestErrPromise, boolean>(true).flatten(); - expectTypeOf(flattened).toEqualTypeOf>(); + expectTypeOf(flattened).toEqualTypeOf>(); await expect(flattened.expectErr("err")).resolves.toEqual(true); }); @@ -165,7 +164,7 @@ describe("flatten", () => { const bar = foo .map((value) => (value === undefined ? Err(new Bar()) : Ok(value))) .flatten(); - expectTypeOf(bar).toEqualTypeOf>(); + expectTypeOf(bar).toEqualTypeOf>(); }); }); @@ -421,11 +420,11 @@ describe("match", () => { describe("all", () => { describe("types", () => { it("infers readonly array", () => { - const result = AsyncResult.all([ + const result = ResultAsync.all([ TestOkPromise(0), TestOkPromise("s"), ]); - expectTypeOf(result).toEqualTypeOf>(); + expectTypeOf(result).toEqualTypeOf>(); }); it("infers array", () => { @@ -433,17 +432,17 @@ describe("all", () => { TestOkPromise(0), TestOkPromise("s"), ]; - const result = AsyncResult.all(array); + const result = ResultAsync.all(array); expectTypeOf(result).toEqualTypeOf< - AsyncResult, number | string> + ResultAsync, number | string> >(); }); }); it("returns the values for an Ok result", async () => { - const result = AsyncResult.all([ - AsyncOk(0), - AsyncOk(1), + const result = ResultAsync.all([ + OkAsync(0), + OkAsync(1), ]); await expect(result.expect("ok")).resolves.toEqual([0, 1]); }); @@ -452,7 +451,7 @@ describe("all", () => { const fn = spy(() => {}); let timeoutId: number; - const slow: AsyncResult = new AsyncResult( + const slow: ResultAsync = new ResultAsync( new Promise((resolve) => { timeoutId = setTimeout( () => { @@ -464,9 +463,9 @@ describe("all", () => { }), ); - const result = await AsyncResult.all([ - AsyncOk(0), - AsyncErr("error"), + const result = await ResultAsync.all([ + OkAsync(0), + ErrAsync("error"), slow, ]); @@ -479,7 +478,7 @@ describe("all", () => { describe("allSettled", () => { describe("types", () => { it("infers readonly array", () => { - const result = AsyncResult.allSettled([ + const result = ResultAsync.allSettled([ TestOkPromise(0), TestOkPromise("s"), ]); @@ -493,7 +492,7 @@ describe("allSettled", () => { TestOkPromise(0), TestOkPromise("s"), ]; - const result = AsyncResult.allSettled(array); + const result = ResultAsync.allSettled(array); expectTypeOf(result).toEqualTypeOf< Promise>> >(); @@ -501,10 +500,10 @@ describe("allSettled", () => { }); it("returns all values", async () => { - const result = AsyncResult.allSettled([ - AsyncOk(0), - AsyncErr("error"), - AsyncOk("s"), + const result = ResultAsync.allSettled([ + OkAsync(0), + ErrAsync("error"), + OkAsync("s"), ]); await expect(result).resolves.toEqual([ Ok(0), @@ -517,11 +516,11 @@ describe("allSettled", () => { describe("any", () => { describe("types", () => { it("infers readonly array", () => { - const result = AsyncResult.any([ + const result = ResultAsync.any([ TestOkPromise(0), TestOkPromise("s"), ]); - expectTypeOf(result).toEqualTypeOf>(); + expectTypeOf(result).toEqualTypeOf>(); }); it("infers array", () => { @@ -529,25 +528,25 @@ describe("any", () => { TestOkPromise(0), TestOkPromise("s"), ]; - const result = AsyncResult.any(array); + const result = ResultAsync.any(array); expectTypeOf(result).toEqualTypeOf< - AsyncResult> + ResultAsync> >(); }); }); it("returns the first Ok result", async () => { - const result = AsyncResult.any([ - AsyncOk(0), - AsyncOk("string"), - AsyncErr("error"), + const result = ResultAsync.any([ + OkAsync(0), + OkAsync("string"), + ErrAsync("error"), ]); await expect(result.expect("ok")).resolves.toEqual(0); }); it("returns the first Err result", async () => { const errors = ["error", "error2"]; - const result = AsyncResult.any(errors.map((error) => AsyncErr(error))); + const result = ResultAsync.any(errors.map((error) => ErrAsync(error))); await expect(result.expectErr("err")).resolves.toEqual(errors); }); }); @@ -555,11 +554,11 @@ describe("any", () => { describe("race", () => { describe("types", () => { it("infers readonly array", () => { - const result = AsyncResult.race([ + const result = ResultAsync.race([ TestOkPromise(0), TestOkPromise("s"), ]); - expectTypeOf(result).toEqualTypeOf>(); + expectTypeOf(result).toEqualTypeOf>(); }); it("infers array", () => { @@ -567,9 +566,9 @@ describe("race", () => { TestOkPromise(0), TestOkPromise("s"), ]; - const result = AsyncResult.race(array); + const result = ResultAsync.race(array); expectTypeOf(result).toEqualTypeOf< - AsyncResult + ResultAsync >(); }); }); @@ -578,7 +577,7 @@ describe("race", () => { let quickTimeoutId: number; let slowTimeoutId: number; - const quick: AsyncResult = new AsyncResult( + const quick: ResultAsync = new ResultAsync( new Promise((resolve) => { quickTimeoutId = setTimeout( resolve, @@ -588,7 +587,7 @@ describe("race", () => { }), ); - const slow: AsyncResult = new AsyncResult( + const slow: ResultAsync = new ResultAsync( new Promise((resolve) => { slowTimeoutId = setTimeout( resolve, @@ -598,7 +597,7 @@ describe("race", () => { }), ); - const result = AsyncResult.race([ + const result = ResultAsync.race([ quick, slow, ]); diff --git a/tests/run.test.ts b/tests/run.test.ts index 2e1b59e..72d2ba7 100644 --- a/tests/run.test.ts +++ b/tests/run.test.ts @@ -3,7 +3,7 @@ import { Err, Ok } from "../src/result.ts"; import { expect } from "@std/expect"; import { test } from "@std/testing/bdd"; import { expectTypeOf } from "expect-type"; -import { AsyncResult } from "../src/async_result.ts"; +import { ResultAsync } from "../src/result_async.ts"; test("runAsync", async () => { const result = runAsync(async () => { @@ -12,7 +12,7 @@ test("runAsync", async () => { } return Err("error"); }); - expectTypeOf(result).toEqualTypeOf>(); + expectTypeOf(result).toEqualTypeOf>(); const result2 = await runAsync(async () => { return Ok(1); diff --git a/tests/try.test.ts b/tests/try.test.ts index 73d9073..b907e92 100644 --- a/tests/try.test.ts +++ b/tests/try.test.ts @@ -2,13 +2,13 @@ import { test } from "@std/testing/bdd"; import { expectTypeOf } from "expect-type"; import { expect } from "@std/expect"; import { - AsyncErr, - AsyncOk, - AsyncResult, Err, + ErrAsync, Ok, + OkAsync, Panic, Result, + ResultAsync, tryBlock, tryBlockAsync, } from "../src/mod.ts"; @@ -89,38 +89,38 @@ test("tryBlockAsync", async () => { const y = yield* oky; return Ok(x + y); }); - expectTypeOf(block5).toEqualTypeOf>(); + expectTypeOf(block5).toEqualTypeOf>(); await expect(block5.expect("ok")).resolves.toEqual(2); const block1 = tryBlockAsync(async function* () { yield* Err("error"); - yield* AsyncErr(2); + yield* ErrAsync(2); if (Math.random() > 0.5) { return Ok("foo"); } else { return Ok(0); } }); - expectTypeOf(block1).toEqualTypeOf>(); + expectTypeOf(block1).toEqualTypeOf>(); const block6 = tryBlockAsync(async function* () { - const okx: AsyncResult = AsyncOk(1); + const okx: ResultAsync = OkAsync(1); const x = yield* okx; - const oky: AsyncResult = AsyncOk(1); + const oky: ResultAsync = OkAsync(1); const y = yield* oky; return Ok(x + y); }); - expectTypeOf(block6).toEqualTypeOf>(); + expectTypeOf(block6).toEqualTypeOf>(); await expect(block6.expect("ok")).resolves.toEqual(2); const block7 = tryBlockAsync(async function* () { - const okx: AsyncResult = AsyncOk(1); + const okx: ResultAsync = OkAsync(1); const x = yield* okx; - const oky: AsyncResult = AsyncErr("error"); + const oky: ResultAsync = ErrAsync("error"); const y = yield* oky; return Ok(x + y); }); - expectTypeOf(block7).toEqualTypeOf>(); + expectTypeOf(block7).toEqualTypeOf>(); await expect(block7.expectErr("err")).resolves.toEqual("error"); // Do not catch panic diff --git a/tests/unwind.test.ts b/tests/unwind.test.ts index fc61432..9088992 100644 --- a/tests/unwind.test.ts +++ b/tests/unwind.test.ts @@ -5,7 +5,7 @@ import { expect } from "@std/expect"; import { catchUnwind, catchUnwindAsync } from "../src/unwind.ts"; import { Result } from "../src/result.ts"; import { Panic } from "../src/error.ts"; -import { AsyncResult } from "../src/async_result.ts"; +import { ResultAsync } from "../src/result_async.ts"; const UNEXPECTED_ERROR_MESSAGE = "Unexpected error type"; @@ -49,7 +49,7 @@ describe("catchUnwind", () => { describe("catchUnwindAsync", () => { it("returns Ok result when async function succeeds", async () => { const result = catchUnwindAsync(async () => 42); - expectTypeOf(result).toEqualTypeOf>(); + expectTypeOf(result).toEqualTypeOf>(); await expect(result.expect("ok")).resolves.toEqual(42); }); diff --git a/tests/util.test.ts b/tests/util.test.ts index 2f48a1b..ace2774 100644 --- a/tests/util.test.ts +++ b/tests/util.test.ts @@ -3,16 +3,16 @@ import { expect } from "@std/expect"; import { expectTypeOf } from "expect-type"; import { Err, Ok } from "../src/result.ts"; import { None, Some } from "../src/option.ts"; -import { AsyncResult } from "../src/async_result.ts"; -import { AsyncOption } from "../src/async_option.ts"; +import { ResultAsync } from "../src/result_async.ts"; +import { OptionAsync } from "../src/option_async.ts"; import { type InferErr, type InferOk, type InferSome, - isAsyncOption, - isAsyncResult, isOption, + isOptionAsync, isResult, + isResultAsync, } from "../src/util.ts"; describe("InferOk", () => { @@ -86,17 +86,17 @@ describe("isResult", () => { }); }); -describe("isAsyncResult", () => { - it("identifies AsyncResult values", () => { - const asyncResult = new AsyncResult(Promise.resolve(Ok(42))); - expect(isAsyncResult(asyncResult)).toBe(true); +describe("isResultAsync", () => { + it("identifies ResultAsync values", () => { + const r = new ResultAsync(Promise.resolve(Ok(42))); + expect(isResultAsync(r)).toBe(true); }); - it("rejects non-AsyncResult values", () => { - expect(isAsyncResult(Ok(42))).toBe(false); - expect(isAsyncResult(Promise.resolve(Ok(42)))).toBe(false); - expect(isAsyncResult(42)).toBe(false); - expect(isAsyncResult(null)).toBe(false); + it("rejects non-ResultAsync values", () => { + expect(isResultAsync(Ok(42))).toBe(false); + expect(isResultAsync(Promise.resolve(Ok(42)))).toBe(false); + expect(isResultAsync(42)).toBe(false); + expect(isResultAsync(null)).toBe(false); }); }); @@ -119,16 +119,16 @@ describe("isOption", () => { }); }); -describe("isAsyncOption", () => { - it("identifies AsyncOption values", () => { - const asyncOption = new AsyncOption(Promise.resolve(Some(42))); - expect(isAsyncOption(asyncOption)).toBe(true); +describe("isOptionAsync", () => { + it("identifies OptionAsync values", () => { + const o = new OptionAsync(Promise.resolve(Some(42))); + expect(isOptionAsync(o)).toBe(true); }); - it("rejects non-AsyncOption values", () => { - expect(isAsyncOption(Some(42))).toBe(false); - expect(isAsyncOption(Promise.resolve(Some(42)))).toBe(false); - expect(isAsyncOption(42)).toBe(false); - expect(isAsyncOption(null)).toBe(false); + it("rejects non-OptionAsync values", () => { + expect(isOptionAsync(Some(42))).toBe(false); + expect(isOptionAsync(Promise.resolve(Some(42)))).toBe(false); + expect(isOptionAsync(42)).toBe(false); + expect(isOptionAsync(null)).toBe(false); }); });