typescript-result
Version:
A Result type inspired by Rust and Kotlin that leverages TypeScript's powerful type system to simplify error handling and make your code more readable and maintainable.
1 lines • 73.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/helpers.ts","../src/result.ts"],"sourcesContent":["/* v8 ignore next */\nexport { Result, AsyncResult } from \"./result.js\";\n/* v8 ignore next */\nexport { assertUnreachable } from \"./helpers.js\";\n","export function isPromise(value: unknown): value is AnyPromise {\n\t/* c8 ignore next */\n\tif (value === null || value === undefined) {\n\t\treturn false;\n\t}\n\n\tif (typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\n\treturn value instanceof Promise || \"then\" in value;\n}\n\nexport function isFunction(value: unknown): value is AnyFunction {\n\treturn typeof value === \"function\";\n}\n\nexport function isAsyncFn(fn: AnyFunction): fn is AnyAsyncFunction {\n\treturn fn.constructor.name === \"AsyncFunction\";\n}\n\n/**\n * Utility function to assert that a case is unreachable\n * @param value the value which to check for exhaustiveness\n *\n * @example\n * ```ts\n * declare const value: \"a\" | \"b\" | \"c\";\n *\n * switch (value) {\n * case \"a\":\n * \t\t// do something\n * \t\t break;\n * case \"b\":\n * \t\t// do something\n * \t\t break;\n * default: assertUnreachable(value) // TS should complain here\n * }\n *\n * ```\n */\nexport function assertUnreachable(value: never): never {\n\tthrow new Error(`Unreachable case: ${value}`);\n}\n\nexport type IsAsyncFunction<T> = T extends AnyAsyncFunction ? true : false;\n\ntype IsPromiseOrAsyncFunction<T> = T extends AnyAsyncFunction\n\t? true\n\t: T extends Promise<any>\n\t\t? true\n\t\t: false;\n\nexport type IsFunction<T> = T extends AnyFunction ? true : false;\n\ntype IsPromise<T> = T extends AnyPromise ? true : false;\n\nexport type UnionContainsPromise<Union> = AnyPromise extends Union\n\t? true\n\t: false;\n\nexport type ListContains<Items extends any[]> = Items[number] extends false\n\t? false\n\t: true;\n\nexport type ListContainsPromiseOrAsyncFunction<T extends any[]> = ListContains<{\n\t[Index in keyof T]: IsPromiseOrAsyncFunction<T[Index]>;\n}>;\n\nexport type ListContainsFunction<T extends any[]> = ListContains<{\n\t[Index in keyof T]: IsFunction<T[Index]>;\n}>;\n\nexport type ListContainsPromise<T extends any[]> = ListContains<{\n\t[Index in keyof T]: IsPromise<T[Index]>;\n}>;\n\nexport type Union<T extends any[]> = T[number];\n\nexport type Unwrap<T> = T extends (...args: any[]) => Promise<infer U>\n\t? U\n\t: T extends (...args: any[]) => infer U\n\t\t? U\n\t\t: T extends Promise<infer U>\n\t\t\t? U\n\t\t\t: T;\n\nexport type UnwrapList<Items extends any[]> = {\n\t[Index in keyof Items]: Unwrap<Items[Index]>;\n};\n\nexport type InferPromise<T> = T extends Promise<infer U> ? U : never;\n\nexport type AnyPromise = Promise<any>;\n\nexport type AnyFunction<Returning = any> = (...args: any[]) => Returning;\nexport type AnyAsyncFunction<Returning = any> = (\n\t...args: any[]\n) => Promise<Returning>;\n\nexport type NativeError = globalThis.Error;\n\n// biome-ignore lint/complexity/noBannedTypes:\nexport type AnyValue = {};\n","import type {\n\tAnyAsyncFunction,\n\tAnyFunction,\n\tAnyPromise,\n\tAnyValue,\n\tInferPromise,\n\tListContainsFunction,\n\tListContainsPromise,\n\tListContainsPromiseOrAsyncFunction,\n\tNativeError,\n\tUnion,\n\tUnionContainsPromise,\n\tUnwrap,\n\tUnwrapList,\n} from \"./helpers.js\";\nimport { isAsyncFn, isFunction, isPromise } from \"./helpers.js\";\n\n// TODO: also add transformError fn to regular map function??\n\ntype InferError<T> = T extends Result<any, infer Error> ? Error : never;\ntype InferValue<T> = T extends Result<infer Value, any> ? Value : T;\n\ntype InferErrors<Items extends any[]> = {\n\t[Index in keyof Items]: InferError<Items[Index]>;\n};\ntype InferValues<Items extends any[]> = {\n\t[Index in keyof Items]: InferValue<Items[Index]>;\n};\n\ntype AnyResult = Result<any, any>;\ntype AnyAsyncResult = AsyncResult<any, any>;\n\ntype ValueOr<Value, Err, Or> = [Err] extends [never]\n\t? [Value] extends [never]\n\t\t? Or\n\t\t: Value\n\t: Value | Or;\n\ntype ErrorOr<Value, Err, Or> = [Value] extends [never]\n\t? [Err] extends [never]\n\t\t? Or\n\t\t: Err\n\t: Err | Or;\n\ntype AccountForFunctionThrowing<Items extends any[]> =\n\tListContainsFunction<Items> extends true\n\t\t? NativeError\n\t\t: ListContainsPromise<Items> extends true\n\t\t\t? NativeError\n\t\t\t: never;\n\n/**\n * Represents the asynchronous outcome of an operation that can either succeed or fail.\n */\nexport class AsyncResult<Value, Err> extends Promise<Result<Value, Err>> {\n\t/**\n\t * Utility getter to infer the value type of the result.\n\t * Note: this getter does not hold any value, it's only used for type inference.\n\t */\n\tdeclare $inferValue: Value;\n\n\t/**\n\t * Utility getter to infer the error type of the result.\n\t * Note: this getter does not hold any value, it's only used for type inference.\n\t */\n\tdeclare $inferError: Err;\n\n\t/**\n\t * Utility getter to check if the current instance is an `AsyncResult`.\n\t */\n\tget isAsyncResult(): true {\n\t\treturn true;\n\t}\n\n\t/**\n\t * @returns the result in a tuple format where the first element is the value and the second element is the error.\n\t * If the result is successful, the error will be `null`. If the result is a failure, the value will be `null`.\n\t *\n\t * This method is especially useful when you want to destructure the result into a tuple and use TypeScript's narrowing capabilities.\n\t *\n\t * @example Narrowing down the result type using destructuring\n\t * ```ts\n\t * declare const result: AsyncResult<number, ErrorA>;\n\t *\n\t * const [value, error] = await result.toTuple();\n\t *\n\t * if (error) {\n\t * // error is ErrorA\n\t * return;\n\t * }\n\t *\n\t * // value must be a number\n\t * ```\n\t */\n\tasync toTuple(): Promise<\n\t\t[Err] extends [never]\n\t\t\t? [value: Value, error: never]\n\t\t\t: [Value] extends [never]\n\t\t\t\t? [value: never, error: Err]\n\t\t\t\t: [value: Value, error: null] | [value: null, error: Err]\n\t> {\n\t\tconst result = await this;\n\t\treturn result.toTuple();\n\t}\n\n\t/**\n\t * @returns the encapsulated error if the result is a failure, otherwise `null`.\n\t */\n\tasync errorOrNull(): Promise<ErrorOr<Value, Err, null>> {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.errorOrNull();\n\t}\n\n\t/**\n\t * @returns the encapsulated value if the result is successful, otherwise `null`.\n\t */\n\tasync getOrNull(): Promise<ValueOr<Value, Err, null>> {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.getOrNull();\n\t}\n\n\t/**\n\t * Retrieves the encapsulated value of the result, or a default value if the result is a failure.\n\t *\n\t * @param defaultValue The value to return if the result is a failure.\n\t *\n\t * @returns The encapsulated value if the result is successful, otherwise the default value.\n\t *\n\t * @example\n\t * obtaining the value of a result, or a default value\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const value = await result.getOrDefault(0); // number\n\t * ```\n\t *\n\t * @example\n\t * using a different type for the default value\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const value = await result.getOrDefault(\"default\"); // number | string\n\t * ```\n\t */\n\tasync getOrDefault<Else>(defaultValue: Value | Else): Promise<Value | Else> {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.getOrDefault(defaultValue);\n\t}\n\n\t/**\n\t * Retrieves the value of the result, or transforms the error using the {@link onFailure} callback into a value.\n\t *\n\t * @param onFailure callback function which allows you to transform the error into a value. The callback can be async as well.\n\t * @returns either the value if the result is successful, or the transformed error.\n\t *\n\t * @example\n\t * transforming the error into a value\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const value = await result.getOrElse((error) => 0); // number\n\t * ```\n\t *\n\t * @example\n\t * using an async callback\n\t * ```ts\n\t * const value = await result.getOrElse(async (error) => 0); // number\n\t * ```\n\t */\n\tasync getOrElse<Else>(onFailure: (error: Err) => Else) {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.getOrElse(onFailure) as Promise<Value | Unwrap<Else>>;\n\t}\n\n\t/**\n\t * Retrieves the encapsulated value of the result, or throws an error if the result is a failure.\n\t *\n\t * @returns The encapsulated value if the result is successful.\n\t *\n\t * @throws the encapsulated error if the result is a failure.\n\t *\n\t * @example\n\t * obtaining the value of a result, or throwing an error\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const value = await result.getOrThrow(); // number\n\t * ```\n\t */\n\tasync getOrThrow(): Promise<Value> {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.getOrThrow();\n\t}\n\n\t/**\n\t * Returns the result of the {@link onSuccess} callback when the result represents success or\n\t * the result of the {@link onFailure} callback when the result represents a failure.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the callbacks are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @param onSuccess callback function to run when the result is successful. The callback can be async as well.\n\t * @param onFailure callback function to run when the result is a failure. The callback can be async as well.\n\t * @returns the result of the callback that was executed.\n\t *\n\t * @example\n\t * folding a result to a response-like object\n\t *\n\t * ```ts\n\t * declare const result: AsyncResult<User, NotFoundError | UserDeactivatedError>;\n\t *\n\t * const response = await result.fold(\n\t * (user) => ({ status: 200, body: user }),\n\t * (error) => {\n\t * switch (error.type) {\n\t * case \"not-found\":\n\t * return { status: 404, body: \"User not found\" };\n\t * case \"user-deactivated\":\n\t * return { status: 403, body: \"User is deactivated\" };\n\t * }\n\t * }\n\t * );\n\t * ```\n\t */\n\tasync fold<SuccessResult, FailureResult>(\n\t\tonSuccess: (value: Value) => SuccessResult,\n\t\tonFailure: (error: Err) => FailureResult,\n\t) {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.fold(onSuccess, onFailure) as Promise<\n\t\t\tUnwrap<SuccessResult> | Unwrap<FailureResult>\n\t\t>;\n\t}\n\n\t/**\n\t * Calls the {@link action} callback when the result represents a failure. It is meant to be used for\n\t * side-effects and the operation does not modify the result itself.\n\t *\n\t * @param action callback function to run when the result is a failure. The callback can be async as well.\n\t * @returns the original instance of the result.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @example\n\t * adding logging between operations\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * result\n\t * .onFailure((error) => console.error(\"I'm failing!\", error))\n\t * .map((value) => value * 2); // proceed with other operations\n\t * ```\n\t */\n\tonFailure(\n\t\taction: (error: Err) => void | Promise<void>,\n\t): AsyncResult<Value, Err> {\n\t\treturn new AsyncResult<Value, Err>((resolve, reject) =>\n\t\t\tthis.then(async (result) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (result.isError()) {\n\t\t\t\t\t\tawait action(result.error as Err);\n\t\t\t\t\t}\n\t\t\t\t\tresolve(result);\n\t\t\t\t} catch (e) {\n\t\t\t\t\treject(e);\n\t\t\t\t}\n\t\t\t}).catch(reject),\n\t\t);\n\t}\n\n\t/**\n\t * Calls the {@link action} callback when the result represents a success. It is meant to be used for\n\t * side-effects and the operation does not modify the result itself.\n\t *\n\t * @param action callback function to run when the result is successful. The callback can be async as well.\n\t * @returns the original instance of the result.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @example\n\t * adding logging between operations\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * result\n\t * .onSuccess((value) => console.log(\"I'm a success!\", value))\n\t * .map((value) => value * 2); // proceed with other operations\n\t * ```\n\t *\n\t * @example\n\t * using an async callback\n\t * ```ts\n\t * declare const result: AsyncResultResult<number, Error>;\n\t *\n\t * const asyncResult = await result.onSuccess(async (value) => someAsyncOperation(value));\n\t * ```\n\t */\n\tonSuccess(\n\t\taction: (value: Value) => void | Promise<void>,\n\t): AsyncResult<Value, Err> {\n\t\treturn new AsyncResult<Value, Err>((resolve, reject) =>\n\t\t\tthis.then(async (result) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (result.isOk()) {\n\t\t\t\t\t\tawait action(result.value as Value);\n\t\t\t\t\t}\n\t\t\t\t\tresolve(result);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t}).catch(reject),\n\t\t);\n\t}\n\n\t/**\n\t * Transforms the value of a successful result using the {@link transform} callback.\n\t * The {@link transform} callback can also return other {@link Result} or {@link AsyncResult} instances,\n\t * which will be returned as-is (the `Error` types will be merged).\n\t * The operation will be ignored if the result represents a failure.\n\t *\n\t * @param transform callback function to transform the value of the result. The callback can be async as well.\n\t * @returns a new {@linkcode AsyncResult} instance with the transformed value\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link transform} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions. Please refer to {@linkcode AsyncResult.mapCatching} for a version that catches exceptions\n\t * > and encapsulates them in a failed result.\n\t *\n\t * @example\n\t * transforming the value of a result\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const transformed = result.map((value) => value * 2); // AsyncResult<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * returning a result instance\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t * declare function multiplyByTwo(value: number): Result<number, Error>;\n\t *\n\t * const transformed = result.map((value) => multiplyByTwo(value)); // AsyncResult<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * doing an async transformation\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t *\n\t * const transformed = result.map(async (value) => value * 2); // AsyncResult<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * returning an async result instance\n\t *\n\t * ```ts\n\t * declare const result: AsyncResult<number, Error>;\n\t * declare function storeValue(value: number): AsyncResult<boolean, Error>;\n\t *\n\t * const transformed = result.map((value) => storeValue(value)); // AsyncResult<boolean, Error>\n\t * ```\n\t */\n\tmap<ReturnType>(transform: (value: Value) => ReturnType) {\n\t\treturn new AsyncResult<any, any>((resolve, reject) =>\n\t\t\tthis.then((result) => {\n\t\t\t\tif (result.isOk()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst returnValue = transform((result as { value: Value }).value);\n\t\t\t\t\t\tif (isPromise(returnValue)) {\n\t\t\t\t\t\t\treturnValue\n\t\t\t\t\t\t\t\t.then((value) =>\n\t\t\t\t\t\t\t\t\tresolve(Result.isResult(value) ? value : Result.ok(value)),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t.catch(reject);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\tResult.isResult(returnValue)\n\t\t\t\t\t\t\t\t\t? returnValue\n\t\t\t\t\t\t\t\t\t: Result.ok(returnValue),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresolve(result);\n\t\t\t\t}\n\t\t\t}).catch(reject),\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError>\n\t\t\t\t: AsyncResult<PromiseValue, Err>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError>\n\t\t\t\t: AsyncResult<ReturnType, Err>;\n\t}\n\n\t/**\n\t * Like {@linkcode AsyncResult.map} it transforms the value of a successful result using the {@link transformValue} callback.\n\t * In addition, it catches any exceptions that might be thrown inside the {@link transformValue} callback and encapsulates them\n\t * in a failed result.\n\t *\n\t * @param transformValue callback function to transform the value of the result. The callback can be async as well.\n\t * @param transformError callback function to transform any potential caught error while transforming the value.\n\t * @returns a new {@linkcode AsyncResult} instance with the transformed value\n\t */\n\tmapCatching<ReturnType, ErrorType = NativeError>(\n\t\ttransformValue: (value: Value) => ReturnType,\n\t\ttransformError?: (error: unknown) => ErrorType,\n\t) {\n\t\treturn new AsyncResult<any, any>((resolve, reject) => {\n\t\t\tthis.map(transformValue)\n\t\t\t\t.then((result: AnyResult) => resolve(result))\n\t\t\t\t.catch((error: unknown) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\tResult.error(transformError ? transformError(error) : error),\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t}) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError | ErrorType>\n\t\t\t\t: AsyncResult<PromiseValue, Err | ErrorType>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError | ErrorType>\n\t\t\t\t: AsyncResult<ReturnType, Err | ErrorType>;\n\t}\n\n\t/**\n\t * Transforms the encapsulated error of a failed result using the {@link transform} callback into a new error.\n\t * This can be useful for instance to capture similar or related errors and treat them as a single higher-level error type\n\t * @param transform callback function to transform the error of the result.\n\t * @returns new {@linkcode AsyncResult} instance with the transformed error.\n\t *\n\t * @example\n\t * transforming the error of a result\n\t * ```ts\n\t * const result = Result.try(() => fetch(\"https://example.com\"))\n\t * .mapCatching((response) => response.json() as Promise<Data>)\n\t * .mapError((error) => new FetchDataError(\"Failed to fetch data\", { cause: error }));\n\t * // AsyncResult<Data, FetchDataError>;\n\t * ```\n\t */\n\tmapError<NewError>(transform: (error: Err) => NewError) {\n\t\treturn new AsyncResult<Value, NewError>((resolve, reject) =>\n\t\t\tthis.then(async (result) => {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(result.mapError(transform));\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t}).catch(reject),\n\t\t);\n\t}\n\n\t/**\n\t * Transforms a failed result using the {@link onFailure} callback into a successful result. Useful for falling back to\n\t * other scenarios when a previous operation fails.\n\t * The {@link onFailure} callback can also return other {@link Result} or {@link AsyncResult} instances,\n\t * which will be returned as-is.\n\t * After a recovery, logically, the result can only be a success. Therefore, the error type is set to `never`, unless\n\t * the {@link onFailure} callback returns a result-instance with another error type.\n\t *\n\t * @param onFailure callback function to transform the error of the result. The callback can be async as well.\n\t * @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance\n\t * if it represents a success.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link onFailure} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions. Please refer to {@linkcode AsyncResult.recoverCatching} for a version that catches exceptions\n\t * > and encapsulates them in a failed result.\n\t *\n\t * @example\n\t * transforming the error into a value\n\t * Note: Since we recover after trying to persist in the database, we can assume that the `DbError` has been taken care\n\t * of and therefore it has been removed from the final result.\n\t * ```ts\n\t * declare function persistInDB(item: Item): AsyncResult<Item, DbError>;\n\t * declare function persistLocally(item: Item): AsyncResult<Item, IOError>;\n\t *\n\t * persistInDB(item).recover(() => persistLocally(item)); // AsyncResult<Item, IOError>\n\t * ```\n\t */\n\trecover<ReturnType>(onFailure: (error: Err) => ReturnType) {\n\t\treturn new AsyncResult((resolve, reject) =>\n\t\t\tthis.then(async (result) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst outcome = await result.recover(onFailure);\n\t\t\t\t\tresolve(outcome);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t}).catch(reject),\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError>\n\t\t\t\t: AsyncResult<PromiseValue | Value, never>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError>\n\t\t\t\t: AsyncResult<Value | ReturnType, never>;\n\t}\n\n\t/**\n\t * Like {@linkcode AsyncResult.recover} it transforms a failed result using the {@link onFailure} callback into a successful result.\n\t * In addition, it catches any exceptions that might be thrown inside the {@link onFailure} callback and encapsulates them\n\t * in a failed result.\n\t *\n\t * @param onFailure callback function to transform the error of the result. The callback can be async as well.\n\t * @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance\n\t * if it represents a success.\n\t */\n\trecoverCatching<ReturnType>(onFailure: (error: Err) => ReturnType) {\n\t\treturn new AsyncResult<any, any>((resolve, reject) =>\n\t\t\tthis.then((result) => {\n\t\t\t\tresolve(result.recoverCatching(onFailure));\n\t\t\t}).catch(reject),\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError | NativeError>\n\t\t\t\t: AsyncResult<PromiseValue | Value, NativeError>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError | NativeError>\n\t\t\t\t: AsyncResult<Value | ReturnType, NativeError>;\n\t}\n\n\t/**\n\t * Print-friendly representation of the `AsyncResult` instance.\n\t */\n\toverride toString(): string {\n\t\treturn \"AsyncResult\";\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tstatic error<Error>(error: Error): AsyncResult<never, Error> {\n\t\treturn new AsyncResult((resolve) => resolve(Result.error(error)));\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tstatic ok<Value>(value: Value): AsyncResult<Value, never> {\n\t\treturn new AsyncResult((resolve) => resolve(Result.ok(value)));\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tstatic fromPromise(promise: AnyPromise) {\n\t\treturn new AsyncResult((resolve, reject) => {\n\t\t\tpromise\n\t\t\t\t.then((value) =>\n\t\t\t\t\tresolve(Result.isResult(value) ? value : Result.ok(value)),\n\t\t\t\t)\n\t\t\t\t.catch(reject);\n\t\t});\n\t}\n\n\t/**\n\t * @internal\n\t */\n\tstatic fromPromiseCatching(\n\t\tpromise: AnyPromise,\n\t\ttransform?: (error: unknown) => unknown,\n\t) {\n\t\treturn new AsyncResult((resolve) => {\n\t\t\tpromise\n\t\t\t\t.then((value) =>\n\t\t\t\t\tresolve(Result.isResult(value) ? value : Result.ok(value)),\n\t\t\t\t)\n\t\t\t\t.catch((caughtError) => {\n\t\t\t\t\tresolve(Result.error(transform?.(caughtError) ?? caughtError));\n\t\t\t\t});\n\t\t});\n\t}\n}\n\n/**\n * Represents the outcome of an operation that can either succeed or fail.\n */\nexport class Result<Value, Err> {\n\tprivate constructor(\n\t\tprivate readonly _value: Value,\n\t\tprivate readonly _error: Err,\n\t) {}\n\n\t/**\n\t * Utility getter to infer the value type of the result.\n\t * Note: this getter does not hold any value, it's only used for type inference.\n\t */\n\tdeclare $inferValue: Value;\n\n\t/**\n\t * Utility getter to infer the error type of the result.\n\t * Note: this getter does not hold any value, it's only used for type inference.\n\t */\n\tdeclare $inferError: Err;\n\n\t/**\n\t * Utility getter that checks if the current instance is a `Result`.\n\t */\n\tget isResult(): true {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Retrieves the encapsulated value of the result.\n\t *\n\t * @returns The value if the operation was successful, otherwise `undefined`.\n\t *\n\t * __Note:__ You can use {@linkcode Result.isOk} to narrow down the type to a successful result.\n\t *\n\t * @example\n\t * obtaining the value of a result, without checking if it's successful\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * result.value; // number | undefined\n\t * ```\n\t *\n\t * @example\n\t * obtaining the value of a result, after checking for success\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * if (result.isOk()) {\n\t * result.value; // number\n\t * }\n\t * ```\n\t */\n\tget value(): ValueOr<Value, Err, undefined> {\n\t\treturn this._value as any;\n\t}\n\n\t/**\n\t * Retrieves the encapsulated error of the result.\n\t *\n\t * @returns The error if the operation failed, otherwise `undefined`.\n\t *\n\t * > [!NOTE]\n\t * > You can use {@linkcode Result.isError} to narrow down the type to a failed result.\n\t *\n\t * @example\n\t * obtaining the value of a result, without checking if it's a failure\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * result.error; // Error | undefined\n\t * ```\n\t *\n\t * @example\n\t * obtaining the error of a result, after checking for failure\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * if (result.isError()) {\n\t * result.error; // Error\n\t * }\n\t * ```\n\t */\n\tget error(): ErrorOr<Value, Err, undefined> {\n\t\treturn this._error as any;\n\t}\n\n\tprivate get success() {\n\t\treturn this.error === undefined;\n\t}\n\n\tprivate get failure() {\n\t\treturn this.error !== undefined;\n\t}\n\n\t/**\n\t * Type guard that checks whether the result is successful.\n\t *\n\t * @returns `true` if the result is successful, otherwise `false`.\n\t *\n\t * @example\n\t * checking if a result is successful\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * if (result.isOk()) {\n\t * \t result.value; // number\n\t * }\n\t * ```\n\t */\n\tisOk(): this is Result<[Value] extends [never] ? AnyValue : Value, never> {\n\t\treturn this.success;\n\t}\n\n\t/**\n\t * Type guard that checks whether the result is successful.\n\t *\n\t * @returns `true` if the result represents a failure, otherwise `false`.\n\t *\n\t * @example\n\t * checking if a result represents a failure\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * if (result.isError()) {\n\t * \t result.error; // Error\n\t * }\n\t * ```\n\t */\n\tisError(): this is Result<never, [Err] extends [never] ? AnyValue : Err> {\n\t\treturn this.failure;\n\t}\n\n\t/**\n\t * @returns the result in a tuple format where the first element is the value and the second element is the error.\n\t * If the result is successful, the error will be `null`. If the result is a failure, the value will be `null`.\n\t *\n\t * This method is especially useful when you want to destructure the result into a tuple and use TypeScript's narrowing capabilities.\n\t *\n\t * @example Narrowing down the result type using destructuring\n\t * ```ts\n\t * declare const result: Result<number, ErrorA>;\n\t *\n\t * const [value, error] = result.toTuple();\n\t *\n\t * if (error) {\n\t * // error is ErrorA\n\t * return;\n\t * }\n\t *\n\t * // value must be a number\n\t * ```\n\t */\n\ttoTuple() {\n\t\treturn [this._value ?? null, this._error ?? null] as [Err] extends [never]\n\t\t\t? [value: Value, error: never]\n\t\t\t: [Value] extends [never]\n\t\t\t\t? [value: never, error: Err]\n\t\t\t\t: [value: Value, error: null] | [value: null, error: Err];\n\t}\n\n\t/**\n\t * @returns the encapsulated error if the result is a failure, otherwise `null`.\n\t */\n\terrorOrNull() {\n\t\treturn (this.failure ? this._error : null) as ErrorOr<Value, Err, null>;\n\t}\n\n\t/**\n\t * @returns the encapsulated value if the result is successful, otherwise `null`.\n\t */\n\tgetOrNull() {\n\t\treturn (this.success ? this._value : null) as ValueOr<Value, Err, null>;\n\t}\n\n\t/**\n\t * Retrieves the value of the result, or a default value if the result is a failure.\n\t *\n\t * @param defaultValue The value to return if the result is a failure.\n\t *\n\t * @returns The encapsulated value if the result is successful, otherwise the default value.\n\t *\n\t * @example\n\t * obtaining the value of a result, or a default value\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const value = result.getOrDefault(0); // number\n\t * ```\n\t *\n\t * @example\n\t * using a different type for the default value\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const value = result.getOrDefault(\"default\"); // number | string\n\t * ```\n\t */\n\tgetOrDefault<Else>(defaultValue: Else): Value | Else {\n\t\treturn this.success ? this._value : defaultValue;\n\t}\n\n\t/**\n\t * Retrieves the value of the result, or transforms the error using the {@link onFailure} callback into a value.\n\t *\n\t * @param onFailure callback function which allows you to transform the error into a value. The callback can be async as well.\n\t * @returns either the value if the result is successful, or the transformed error.\n\t *\n\t * @example\n\t * transforming the error into a value\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const value = result.getOrElse((error) => 0); // number\n\t * ```\n\t *\n\t * @example\n\t * using an async callback\n\t * ```ts\n\t * const value = await result.getOrElse(async (error) => 0); // Promise<number>\n\t * ```\n\t */\n\tgetOrElse<Else>(\n\t\tonFailure: (error: Err) => Else,\n\t): Else extends Promise<infer U> ? Promise<Value | U> : Value | Else {\n\t\tif (isAsyncFn(onFailure)) {\n\t\t\treturn this.success\n\t\t\t\t? Promise.resolve(this._value)\n\t\t\t\t: (onFailure(this._error) as any);\n\t\t}\n\n\t\treturn this.success ? this._value : (onFailure(this._error) as any);\n\t}\n\n\t/**\n\t * Retrieves the value of the result, or throws an error if the result is a failure.\n\t *\n\t * @returns The value if the result is successful.\n\t *\n\t * @throws the encapsulated error if the result is a failure.\n\t *\n\t * @example\n\t * obtaining the value of a result, or throwing an error\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const value = result.getOrThrow(); // number\n\t * ```\n\t */\n\tgetOrThrow(): Value {\n\t\tif (this.success) {\n\t\t\treturn this._value;\n\t\t}\n\n\t\tthrow this._error;\n\t}\n\n\t/**\n\t * Returns the result of the {@link onSuccess} callback when the result represents success or\n\t * the result of the {@link onFailure} callback when the result represents a failure.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the callbacks are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @param onSuccess callback function to run when the result is successful. The callback can be async as well.\n\t * @param onFailure callback function to run when the result is a failure. The callback can be async as well.\n\t * @returns the result of the callback that was executed.\n\t *\n\t * @example\n\t * folding a result to a response-like object\n\t *\n\t * ```ts\n\t * declare const result: Result<User, NotFoundError | UserDeactivatedError>;\n\t *\n\t * const response = result.fold(\n\t * (user) => ({ status: 200, body: user }),\n\t * (error) => {\n\t * switch (error.type) {\n\t * case \"not-found\":\n\t * return { status: 404, body: \"User not found\" };\n\t * case \"user-deactivated\":\n\t * return { status: 403, body: \"User is deactivated\" };\n\t * }\n\t * }\n\t * );\n\t * ```\n\t */\n\tfold<SuccessResult, FailureResult>(\n\t\tonSuccess: (value: Value) => SuccessResult,\n\t\tonFailure: (error: Err) => FailureResult,\n\t) {\n\t\tconst isAsync = isAsyncFn(onSuccess) || isAsyncFn(onFailure);\n\n\t\tconst outcome = this.success\n\t\t\t? onSuccess(this._value)\n\t\t\t: onFailure(this._error);\n\n\t\treturn (\n\t\t\tisAsync && !isPromise(outcome) ? Promise.resolve(outcome) : outcome\n\t\t) as UnionContainsPromise<SuccessResult | FailureResult> extends true\n\t\t\t? Promise<Unwrap<SuccessResult> | Unwrap<FailureResult>>\n\t\t\t: SuccessResult | FailureResult;\n\t}\n\n\t/**\n\t * Calls the {@link action} callback when the result represents a failure. It is meant to be used for\n\t * side-effects and the operation does not modify the result itself.\n\t *\n\t * @param action callback function to run when the result is a failure. The callback can be async as well.\n\t * @returns the original instance of the result.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @example\n\t * adding logging between operations\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * result\n\t * .onFailure((error) => console.error(\"I'm failing!\", error))\n\t * .map((value) => value * 2); // proceed with other operations\n\t * ```\n\t */\n\tonFailure<ReturnValue>(\n\t\taction: (error: Err) => ReturnValue,\n\t): ReturnValue extends AnyPromise ? AsyncResult<Value, Err> : this {\n\t\tconst isAsync = isAsyncFn(action);\n\n\t\tif (this.failure) {\n\t\t\tconst outcome = action(this._error);\n\t\t\tif (isAsync) {\n\t\t\t\treturn new AsyncResult((resolve) => {\n\t\t\t\t\t(outcome as AnyPromise).then(() =>\n\t\t\t\t\t\tresolve(Result.error(this._error)),\n\t\t\t\t\t);\n\t\t\t\t}) as any;\n\t\t\t}\n\n\t\t\treturn this as any;\n\t\t}\n\n\t\treturn isAsync ? AsyncResult.ok(this._value) : (this as any);\n\t}\n\n\t/**\n\t * Calls the {@link action} callback when the result represents a success. It is meant to be used for\n\t * side-effects and the operation does not modify the result itself.\n\t *\n\t * @param action callback function to run when the result is successful. The callback can be async as well.\n\t * @returns the original instance of the result. If the callback is async, it returns a new {@link AsyncResult} instance.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions\n\t *\n\t * @example\n\t * adding logging between operations\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * result\n\t * .onSuccess((value) => console.log(\"I'm a success!\", value))\n\t * .map((value) => value * 2); // proceed with other operations\n\t * ```\n\t *\n\t * @example\n\t * using an async callback\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const asyncResult = await result.onSuccess(async (value) => someAsyncOperation(value));\n\t * ```\n\t */\n\tonSuccess(action: (value: Value) => Promise<void>): AsyncResult<Value, Err>;\n\tonSuccess(action: (value: Value) => void): this;\n\tonSuccess(action: (value: Value) => unknown): unknown {\n\t\tconst isAsync = isAsyncFn(action);\n\n\t\tif (this.success) {\n\t\t\tconst outcome = action(this._value);\n\t\t\tif (isAsync) {\n\t\t\t\treturn new AsyncResult((resolve) => {\n\t\t\t\t\t(outcome as AnyPromise).then(() => resolve(Result.ok(this._value)));\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\treturn isAsync ? AsyncResult.error(this._error) : this;\n\t}\n\n\t/**\n\t * Transforms the value of a successful result using the {@link transform} callback.\n\t * The {@link transform} callback can also return other {@link Result} or {@link AsyncResult} instances,\n\t * which will be returned as-is (the `Error` types will be merged).\n\t * The operation will be ignored if the result represents a failure.\n\t *\n\t * @param transform callback function to transform the value of the result. The callback can be async as well.\n\t * @returns a new {@linkcode Result} instance with the transformed value, or a new {@linkcode AsyncResult} instance\n\t * if the transform function is async.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link transform} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions. Please refer to {@linkcode Result.mapCatching} for a version that catches exceptions\n\t * > and encapsulates them in a failed result.\n\t *\n\t * @example\n\t * transforming the value of a result\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const transformed = result.map((value) => value * 2); // Result<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * returning a result instance\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t * declare function multiplyByTwo(value: number): Result<number, Error>;\n\t *\n\t * const transformed = result.map((value) => multiplyByTwo(value)); // Result<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * doing an async transformation\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t *\n\t * const transformed = result.map(async (value) => value * 2); // AsyncResult<number, Error>\n\t * ```\n\t *\n\t * @example\n\t * returning an async result instance\n\t *\n\t * ```ts\n\t * declare const result: Result<number, Error>;\n\t * declare function storeValue(value: number): AsyncResult<boolean, Error>;\n\t *\n\t * const transformed = result.map((value) => storeValue(value)); // AsyncResult<boolean, Error>\n\t * ```\n\t */\n\tmap<ReturnType>(transform: (value: Value) => ReturnType) {\n\t\treturn (\n\t\t\tthis.success\n\t\t\t\t? Result.run(() => transform(this._value))\n\t\t\t\t: isAsyncFn(transform)\n\t\t\t\t\t? AsyncResult.error(this._error)\n\t\t\t\t\t: this\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError>\n\t\t\t\t: AsyncResult<PromiseValue, Err>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? Result<ResultValue, Err | ResultError>\n\t\t\t\t: Result<ReturnType, Err>;\n\t}\n\n\t/**\n\t * Like {@linkcode Result.map} it transforms the value of a successful result using the {@link transformValue} callback.\n\t * In addition, it catches any exceptions that might be thrown inside the {@link transformValue} callback and encapsulates them\n\t * in a failed result.\n\t *\n\t * @param transformValue callback function to transform the value of the result. The callback can be async as well.\n\t * @param transformError callback function to transform any potential caught error while transforming the value.\n\t * @returns a new {@linkcode Result} instance with the transformed value, or a new {@linkcode AsyncResult} instance\n\t * if the transform function is async.\n\t */\n\tmapCatching<ReturnType, ErrorType = NativeError>(\n\t\ttransformValue: (value: Value) => ReturnType,\n\t\ttransformError?: (err: unknown) => ErrorType,\n\t) {\n\t\treturn (\n\t\t\tthis.success\n\t\t\t\t? Result.try(\n\t\t\t\t\t\t() => transformValue(this._value),\n\t\t\t\t\t\ttransformError as AnyFunction,\n\t\t\t\t\t)\n\t\t\t\t: this\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<ResultValue, Err | ResultError | ErrorType>\n\t\t\t\t: AsyncResult<PromiseValue, Err | ErrorType>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? Result<ResultValue, Err | ResultError | ErrorType>\n\t\t\t\t: Result<ReturnType, Err | ErrorType>;\n\t}\n\n\t/**\n\t * Transforms the encapsulated error of a failed result using the {@link transform} callback into a new error.\n\t * This can be useful for instance to capture similar or related errors and treat them as a single higher-level error type\n\t * @param transform callback function to transform the error of the result.\n\t * @returns new {@linkcode Result} instance with the transformed error.\n\t *\n\t * @example\n\t * transforming the error of a result\n\t * ```ts\n\t * declare const result: Result<number, ErrorA>;\n\t *\n\t * result.mapError((error) => new ErrorB(error.message)); // Result<number, ErrorB>\n\t * ```\n\t */\n\tmapError<NewError>(\n\t\ttransform: (error: Err) => NewError,\n\t): Result<Value, NewError> {\n\t\tif (this.success) {\n\t\t\treturn this as Result<Value, any>;\n\t\t}\n\n\t\treturn Result.error(transform(this._error));\n\t}\n\n\t/**\n\t * Transforms a failed result using the {@link onFailure} callback into a successful result. Useful for falling back to\n\t * other scenarios when a previous operation fails.\n\t * The {@link onFailure} callback can also return other {@link Result} or {@link AsyncResult} instances,\n\t * which will be returned as-is.\n\t * After a recovery, logically, the result can only be a success. Therefore, the error type is set to `never`, unless\n\t * the {@link onFailure} callback returns a result-instance with another error type.\n\t *\n\t * @param onFailure callback function to transform the error of the result. The callback can be async as well.\n\t * @returns a new successful {@linkcode Result} instance or a new successful {@linkcode AsyncResult} instance\n\t * when the result represents a failure, or the original instance if it represents a success.\n\t *\n\t * > [!NOTE]\n\t * > Any exceptions that might be thrown inside the {@link onFailure} callback are not caught, so it is your responsibility\n\t * > to handle these exceptions. Please refer to {@linkcode Result.recoverCatching} for a version that catches exceptions\n\t * > and encapsulates them in a failed result.\n\t *\n\t * @example\n\t * transforming the error into a value\n\t * Note: Since we recover after trying to persist in the database, we can assume that the `DbError` has been taken care\n\t * of and therefore it has been removed from the final result.\n\t * ```ts\n\t * declare function persistInDB(item: Item): Result<Item, DbError>;\n\t * declare function persistLocally(item: Item): Result<Item, IOError>;\n\t *\n\t * persistInDB(item).recover(() => persistLocally(item)); // Result<Item, IOError>\n\t * ```\n\t */\n\trecover<ReturnType>(onFailure: (error: Err) => ReturnType) {\n\t\treturn (\n\t\t\tthis.success\n\t\t\t\t? isAsyncFn(onFailure)\n\t\t\t\t\t? AsyncResult.ok(this._value)\n\t\t\t\t\t: this\n\t\t\t\t: Result.run(() => onFailure(this._error))\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError>\n\t\t\t\t: AsyncResult<PromiseValue | Value, never>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? Result<Value | ResultValue, ResultError>\n\t\t\t\t: Result<Value | ReturnType, never>;\n\t}\n\n\t/**\n\t * Like {@linkcode Result.recover} it transforms a failed result using the {@link onFailure} callback into a successful result.\n\t * In addition, it catches any exceptions that might be thrown inside the {@link onFailure} callback and encapsulates them\n\t * in a failed result.\n\t *\n\t * @param onFailure callback function to transform the error of the result. The callback can be async as well.\n\t * @returns a new successful {@linkcode Result} instance or a new successful {@linkcode AsyncResult} instance\n\t * when the result represents a failure, or the original instance if it represents a success.\n\t */\n\trecoverCatching<ReturnType>(onFailure: (error: Err) => ReturnType) {\n\t\treturn (\n\t\t\tthis.success\n\t\t\t\t? isAsyncFn(onFailure)\n\t\t\t\t\t? AsyncResult.ok(this._value)\n\t\t\t\t\t: this\n\t\t\t\t: Result.try(() => onFailure(this._error))\n\t\t) as ReturnType extends Promise<infer PromiseValue>\n\t\t\t? PromiseValue extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? AsyncResult<Value | ResultValue, ResultError | NativeError>\n\t\t\t\t: AsyncResult<PromiseValue | Value, NativeError>\n\t\t\t: ReturnType extends Result<infer ResultValue, infer ResultError>\n\t\t\t\t? Result<Value | ResultValue, ResultError | NativeError>\n\t\t\t\t: Result<Value | ReturnType, NativeError>;\n\t}\n\n\t/**\n\t * Returns a string representation of the result.\n\t */\n\ttoString(): string {\n\t\tif (this.success) {\n\t\t\treturn `Result.ok(${this._value})`;\n\t\t}\n\n\t\treturn `Result.error(${this.error})`;\n\t}\n\n\t/**\n\t * Creates a new result instance that represents a successful outcome.\n\t *\n\t * @param value The value to encapsulate in the result.\n\t * @returns a new {@linkcode Result} instance.\n\t *\n\t * @example\n\t * ```ts\n\t * const result = Result.ok(42); // Result<number, never>\n\t * ```\n\t */\n\tstatic ok(): Result<void, never>;\n\tstatic ok<Value>(value: Value): Result<Value, never>;\n\tstatic ok(value?: unknown) {\n\t\treturn new Result(value, undefined);\n\t}\n\n\t/**\n\t * Creates a new result instance that represents a failed outcome.\n\t *\n\t * @param error The error to encapsulate in the result.\n\t * @returns a new {@linkcode Result} instance.\n\t *\n\t * @example\n\t * ```ts\n\t * const result = Result.error(new NotFoundError()); // Result<never, NotFoundError>\n\t * ```\n\t */\n\tstatic error<Error>(error: Error): Result<never, Error> {\n\t\treturn new Result(undefined as never, error);\n\t}\n\n\t/**\n\t * Type guard that checks whether the provided value is a {@linkcode Result} instance.\n\t *\n\t * @param possibleResult any value that might be a {@linkcode Result} instance.\n\t * @returns true if the provided value is a {@linkcode Result} instance, otherwise false.\n\t */\n\tstatic isResult(possibleResult: unknown): possibleResult is AnyResult {\n\t\treturn possibleResult instanceof Result;\n\t}\n\n\t/**\n\t * Type guard that checks whether the provided value is a {@linkcode AsyncResult} instance.\n\t *\n\t * @param possibleAsyncResult any value that might be a {@linkcode AsyncResult} instance.\n\t * @returns true if the provided value is a {@linkcode AsyncResult} instance, otherwise false.\n\t */\n\tstatic isAsyncResult(\n\t\tpossibleAsyncResult: unknown,\n\t): possibleAsyncResult is AnyAsyncResult {\n\t\treturn possibleAsyncResult instanceof AsyncResult;\n\t}\n\n\tprivate static run(fn: AnyFunction): AnyResult | AnyAsyncResult {\n\t\tconst returnValue = fn();\n\n\t\tif (isPromise(returnValue)) {\n\t\t\treturn AsyncResult.fromPromise(returnValue);\n\t\t}\n\n\t\treturn Result.isResult(returnValue) ? returnValue : Result.ok(returnValue);\n\t}\n\n\tprivate static allInternal(\n\t\titems: any[],\n\t\topts: { catching: boolean },\n\t): AnyResult | AnyAsyncResult {\n\t\tconst runner = opts.catching ? Result.try : Result.run;\n\n\t\tconst flattened: Array<AnyResult | AnyAsyncResult> = [];\n\n\t\tlet isAsync = items.some(isPromise);\n\t\tlet hasFailure = false;\n\n\t\tfor (const item of items) {\n\t\t\tif (isFunction(item)) {\n\t\t\t\tif (hasFailure) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst returnValue = runner(item as AnyFunction);\n\n\t\t\t\tif (Result.isResult(returnValue) && returnValue.isError()) {\n\t\t\t\t\thasFailure = true;\n\t\t\t\t\tif (!isAsync) {\n\t\t\t\t\t\treturn returnValue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (Result.isAsyncResult(returnValue)) {\n\t\t\t\t\tisAsync = true;\n\t\t\t\t}\n\n\t\t\t\tflattened.push(returnValue);\n\t\t\t} else if (Result.isResult(item)) {\n\t\t\t\tif (item.isError()) {\n\t\t\t\t\thasFailure = true;\n\t\t\t\t\tif (!isAsync) {\n\t\t\t\t\t\treturn item;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tflattened.push(item);\n\t\t\t} else if (Result.isAsyncResult(item)) {\n\t\t\t\tisAsync = true;\n\t\t\t\tflattened.push(item);\n\t\t\t} else if (isPromise(item)) {\n\t\t\t\tisAsync = true;\n\n\t\t\t\tflattened.push(\n\t\t\t\t\topts.catching\n\t\t\t\t\t\t? AsyncResult.fromPromiseCatching(item)\n\t\t\t\t\t\t: AsyncResult.fromPromise(item),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tflattened.push(Result.ok(item));\n\t\t\t}\n\t\t}\n\n\t\tif (isAsync) {\n\t\t\treturn new AsyncResult((resolve, reject) => {\n\t\t\t\tconst asyncResults: AnyAsyncResult[] = [];\n\t\t\t\tconst asyncIndexes: number[] = [];\n\n\t\t\t\tfor (let i = 0; i < flattened.length; i++) {\n\t\t\t\t\tconst item = flattened[i];\n\t\t\t\t\tif (Result.isAsyncResult(item)) {\n\t\t\t\t\t\tasyncResults.push(item);\n\t\t\t\t\t\tasyncIndexes.push(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tPromise.all(asyncResults)\n\t\t\t\t\t.then((resolvedResults) => {\n\t\t\t\t\t\tconst merged = [...flattened] as AnyResult[];\n\t\t\t\t\t\tfor (let i = 0; i < resolvedResults.length; i++) {\n\t\t\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion:\n\t\t\t\t\t\t\tmerged[asyncIndexes[i]!] = resolvedResults[i]!;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst firstFailedResult = merged.find((resolvedResult) =>\n\t\t\t\t\t\t\tresolvedResult.isError(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (firstFailedResult) {\n\t\t\t\t\t\t\tresolve(