UNPKG

typescript-result

Version:

Supercharge your TypeScript error handling with a powerful Result type that transforms chaotic try-catch blocks into elegant, type-safe code.

1 lines 107 kB
{"version":3,"sources":["../src/index.ts","../src/helpers.ts","../src/matcher.ts","../src/result.ts"],"sourcesContent":["export { assertUnreachable } from \"./helpers.js\";\n\nimport {\n\ttype AsyncResult,\n\ttype IfGeneratorAsync,\n\ttype InferGeneratorError,\n\ttype InferGeneratorReturn,\n\ttype Result as ResultBase,\n\tResultFactory,\n} from \"./result.js\";\n\nexport { NonExhaustiveError } from \"./matcher.js\";\nexport { AsyncResult } from \"./result.js\";\n\nexport namespace Result {\n\texport type Error<E> = ResultBase<never, E>;\n\texport type Ok<V> = ResultBase<V, never>;\n\texport type InferError<T> = T extends AsyncResult<any, infer Error>\n\t\t? Error\n\t\t: T extends Result<any, infer Error>\n\t\t\t? Error\n\t\t\t: never;\n\texport type InferValue<T> = T extends AsyncResult<infer Value, any>\n\t\t? Value\n\t\t: T extends Result<infer Value, any>\n\t\t\t? Value\n\t\t\t: T;\n\texport type InferResultFromGenerator<T> = T extends Generator | AsyncGenerator\n\t\t? IfGeneratorAsync<\n\t\t\t\tT,\n\t\t\t\tAsyncResult<InferGeneratorReturn<T>, InferGeneratorError<T>>,\n\t\t\t\tResult<InferGeneratorReturn<T>, InferGeneratorError<T>>\n\t\t\t>\n\t\t: never;\n}\n\ntype Ok<Value> = [Value] extends [never] ? never : Result.Ok<Value>;\ntype Error<Err> = [Err] extends [never] ? never : Result.Error<Err>;\n\nexport type Result<Value, Err> = Ok<Value> | Error<Err>;\n\nexport const Result: typeof ResultFactory = ResultFactory;\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\nexport function isGenerator(obj: any): obj is Generator {\n\treturn (\n\t\ttypeof obj === \"object\" &&\n\t\tobj !== null &&\n\t\ttypeof obj.next === \"function\" &&\n\t\ttypeof obj.throw === \"function\" &&\n\t\ttypeof obj.return === \"function\" &&\n\t\ttypeof obj[Symbol.iterator] === \"function\" &&\n\t\tobj[Symbol.iterator]() === obj\n\t);\n}\n\nexport function isAsyncGenerator(obj: any): obj is AsyncGenerator {\n\treturn (\n\t\ttypeof obj === \"object\" &&\n\t\tobj !== null &&\n\t\ttypeof obj.next === \"function\" &&\n\t\ttypeof obj.throw === \"function\" &&\n\t\ttypeof obj.return === \"function\" &&\n\t\ttypeof obj[Symbol.asyncIterator] === \"function\" &&\n\t\tobj[Symbol.asyncIterator]() === obj\n\t);\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\nexport type IsFunction<T> = T extends AnyFunction ? true : false;\n\nexport type AnyPromise = Promise<any>;\n\nexport type Constructor<T> = abstract new (...args: any[]) => T;\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\nexport type AnyValue = {};\n\nexport type Contains<T, V, U = T> = (\n\tT extends U\n\t\t? U extends V\n\t\t\t? true\n\t\t\t: false\n\t\t: false\n) extends false\n\t? false\n\t: true;\n","import {\n\ttype AnyPromise,\n\ttype Constructor,\n\ttype Contains,\n\tisAsyncFn,\n\tisFunction,\n\tisPromise,\n} from \"./helpers.js\";\n\nexport class NonExhaustiveError<E> extends Error {\n\tconstructor(public readonly error: E) {\n\t\tsuper(\"Not all error cases were handled\");\n\t}\n}\n\nexport class RedundantElseClauseError<T> extends Error {\n\t/* c8 ignore next 3 */\n\tconstructor(public readonly error: T) {\n\t\tsuper();\n\t}\n}\n\ntype ExtractHandledCases<T extends readonly any[]> = {\n\t[I in keyof T]: T[I] extends Constructor<infer U> ? U : T[I];\n}[number];\n\ntype WhenValue<E> = Constructor<Extract<E, object>> | E;\n\nexport class Matcher<out E, InferredOutput = never> {\n\tprivate cases: { value: unknown; handler: (error: unknown) => any }[] = [];\n\tprivate defaultHandler: ((error: any) => any) | undefined = undefined;\n\n\t/**\n\t * @internal\n\t */\n\tconstructor(private error: E) {}\n\n\t/**\n\t * Let's you match against one or more error types.\n\t * If the error matches one of the provided types, the corresponding handler will be called.\n\t * The last argument must be a handler function, and the preceding arguments must be either\n\t * the error class (constructor) or a literal value (e.g. string).\n\t *\n\t * @example\n\t * Match using the class of the error:\n\t * ```ts\n\t * class ErrorA extends Error {\n\t * readonly type = \"error-a\";\n\t * }\n\t *\n\t * matcher.when(ErrorA, (error) => console.log(\"Handled ErrorA:\", error));\n\t * ```\n\t *\n\t * @example\n\t * Match on multiple error classes with a single handler:\n\t * ```ts\n\t * class ErrorA extends Error {\n\t * readonly type = \"error-a\";\n\t * }\n\t *\n\t * class ErrorB extends Error {\n\t * readonly type = \"error-b\";\n\t * }\n\t *\n\t * matcher.when(ErrorA, ErrorB, (error) => {\n\t * console.log(\"Handled ErrorA or ErrorB:\", error);\n\t * });\n\t * ```\n\t *\n\t * @example\n\t * Match using a literal value:\n\t * ```ts\n\t * matcher.when(\"SOME_ERROR_CODE\", (error) => console.log(\"Handled error with code:\", error));\n\t * ```\n\t */\n\twhen<\n\t\tT extends WhenValue<E>,\n\t\tU extends readonly WhenValue<E>[],\n\t\tR,\n\t\tHandledCases = ExtractHandledCases<[T, ...U]>,\n\t>(value: T, ...args: [...rest: U, handler: (error: HandledCases) => R]) {\n\t\tconst cases = [value, ...args.slice(0, -1)] as unknown[];\n\t\tconst handler = args.at(-1) as (error: unknown) => R;\n\t\tthis.cases.push(...cases.map((value) => ({ value, handler })));\n\t\treturn this as Matcher<Exclude<E, HandledCases>, InferredOutput | R>;\n\t}\n\n\t/**\n\t * Registers a handler that will be called if no other case matches.\n\t * Note: you can only register one `else` handler, otherwise it will throw an error.\n\t * Note: TS will complain if you try to register an `else` handler when all error cases\n\t * are already handled.\n\t *\n\t * @example\n\t * ```ts\n\t * matcher.else((error) =>\n\t * console.log(\"Handled any other error:\", error);\n\t * );\n\t * ```\n\t */\n\treadonly else = ((handler) => {\n\t\tif (this.defaultHandler) {\n\t\t\tthrow new Error(\"already registered an 'else' handler\");\n\t\t}\n\n\t\tthis.defaultHandler = handler;\n\t\treturn this as any;\n\t}) as [E] extends [never]\n\t\t? RedundantElseClauseError<\"All error cases are already handled\">\n\t\t: <R>(handler: (error: E) => R) => Matcher<never, InferredOutput | R>;\n\n\t/**\n\t * Executes the matcher and returns the result of the first matching handler.\n\t * If no handler matches, it will call the `else` handler if registered,\n\t * or throw a `NonExhaustiveError` if no `else` handler is registered.\n\t *\n\t * Note: If not all error cases are handled, this will throw a `NonExhaustiveError`,\n\t * and TS will complain with a helpful message indicating which cases are not handled.\n\t */\n\treadonly run = (() => {\n\t\tconst isAsync = this.cases.some((item) => isAsyncFn(item.handler));\n\n\t\tfor (const item of this.cases) {\n\t\t\tconst match =\n\t\t\t\t(isFunction(item.value) && this.error instanceof item.value) ||\n\t\t\t\titem.value === this.error;\n\n\t\t\tif (match) {\n\t\t\t\tconst value = item.handler(this.error);\n\t\t\t\treturn isPromise(value)\n\t\t\t\t\t? value\n\t\t\t\t\t: isAsync\n\t\t\t\t\t\t? Promise.resolve(value)\n\t\t\t\t\t\t: value;\n\t\t\t}\n\t\t}\n\n\t\tif (this.defaultHandler) {\n\t\t\treturn this.defaultHandler(this.error);\n\t\t}\n\n\t\tthrow new NonExhaustiveError(this.error);\n\t}) as [E] extends [never]\n\t\t? () => Contains<InferredOutput, AnyPromise> extends true\n\t\t\t\t? Promise<Awaited<InferredOutput>>\n\t\t\t\t: InferredOutput\n\t\t: NonExhaustiveError<E>;\n}\n","import type {\n\tAnyAsyncFunction,\n\tAnyFunction,\n\tAnyPromise,\n\tAnyValue,\n\tContains,\n\tNativeError,\n} from \"./helpers.js\";\nimport {\n\tisAsyncFn,\n\tisAsyncGenerator,\n\tisFunction,\n\tisGenerator,\n\tisPromise,\n} from \"./helpers.js\";\nimport type { Result as OuterResult } from \"./index.js\";\nimport { Matcher } from \"./matcher.js\";\n\ntype InferError<T> = T extends AsyncResult<any, infer Error>\n\t? Error\n\t: T extends Result<any, infer Error>\n\t\t? Error\n\t\t: never;\ntype InferValue<T> = T extends AsyncResult<infer Value, any>\n\t? Value\n\t: T extends Result<infer Value, any>\n\t\t? Value\n\t\t: T;\n\ntype AnyResult = Result<any, any>;\ntype AnyOuterResult = OuterResult<any, any>;\ntype AnyAsyncResult = AsyncResult<any, any>;\n\ntype ReturningValue<T> =\n\t| Result<T, any>\n\t| AsyncResult<T, any>\n\t| Promise<ReturningValue<T>>\n\t| T;\n\ntype ReturningError<T> =\n\t| Result<any, T>\n\t| AsyncResult<any, T>\n\t| Promise<ReturningError<T>>;\n\ntype ExtractValue<T> = T extends ReturningValue<infer Value> ? Value : T;\ntype ExtractError<T> = T extends ReturningError<infer Error> ? Error : never;\n\ntype ExtractValues<T extends any[]> = {\n\t[I in keyof T]: T[I] extends Generator | AsyncGenerator\n\t\t? InferGeneratorReturn<T[I]>\n\t\t: ExtractValue<T[I]>;\n};\ntype ExtractErrors<T extends any[]> = {\n\t[I in keyof T]: T[I] extends Generator | AsyncGenerator\n\t\t? InferGeneratorError<T[I]>\n\t\t: ExtractError<T[I]>;\n};\n\ntype ReturnsAsync<T> = Contains<\n\tT,\n\tAnyAsyncResult | AnyPromise | AsyncGenerator\n>;\ntype IfReturnsAsync<T, Yes, No> = ReturnsAsync<T> extends true ? Yes : No;\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 SyncOrAsyncGenerator<Y, R, N> =\n\t| Generator<Y, R, N>\n\t| AsyncGenerator<Y, R, N>;\n\nexport type InferGeneratorReturn<T> = T extends SyncOrAsyncGenerator<\n\tany,\n\tinfer R,\n\tany\n>\n\t? ExtractValue<R>\n\t: never;\n\nexport type InferGeneratorError<T> = [T] extends [\n\tSyncOrAsyncGenerator<never, infer R, any>,\n]\n\t? InferError<R>\n\t: T extends SyncOrAsyncGenerator<{ error: infer E }, infer R, any>\n\t\t? E | InferError<R>\n\t\t: never;\n\ntype IsGeneratorAsync<T> = T extends SyncOrAsyncGenerator<\n\tinfer Info,\n\tinfer R,\n\tany\n>\n\t? Contains<Info, { async: true }> extends true\n\t\t? true\n\t\t: Contains<T, AsyncGenerator<any, any, any>> extends true\n\t\t\t? true\n\t\t\t: Contains<R, AnyAsyncResult> extends true\n\t\t\t\t? true\n\t\t\t\t: false\n\t: false;\n\nexport type IfGeneratorAsync<T, Yes, No> = IsGeneratorAsync<T> extends true\n\t? Yes\n\t: No;\n\ntype UnwrapList<T extends any[]> = {\n\t[I in keyof T]: T[I] extends AnyFunction<infer U> ? U : T[I];\n};\n\ntype IsAsync<T> = IsGeneratorAsync<T> extends true\n\t? true\n\t: T extends AnyPromise\n\t\t? true\n\t\t: T extends AnyFunction<infer U>\n\t\t\t? IsAsync<U>\n\t\t\t: never;\n\ntype ListContainsAsync<T extends any[]> = {\n\t[I in keyof T]: IsAsync<T[I]>;\n}[number] extends false\n\t? false\n\t: true;\n\ntype AccountForThrowing<T extends any[]> = {\n\t[I in keyof T]: T[I] extends AnyFunction | AnyPromise ? true : false;\n}[number] extends false\n\t? never\n\t: NativeError;\n\n/**\n * Represents the asynchronous outcome of an operation that can either succeed or fail.\n */\nexport class AsyncResult<Value, Err> extends Promise<OuterResult<Value, Err>> {\n\t/**\n\t * @internal\n\t */\n\tconstructor(executor: ConstructorParameters<typeof Promise>[0]) {\n\t\tsuper(executor as any);\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*[Symbol.iterator](): Generator<{ error: Err; async: true }, Value, any> {\n\t\treturn yield this as any;\n\t}\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<\n\t\tThis extends AnyAsyncResult,\n\t\tV = InferValue<This>,\n\t\tE = InferError<This>,\n\t>(this: This) {\n\t\tconst result = (await this) as Result<Value, Err>;\n\t\treturn result.toTuple() as [E] extends [never]\n\t\t\t? [value: V, error: never]\n\t\t\t: [V] extends [never]\n\t\t\t\t? [value: never, error: E]\n\t\t\t\t: [value: V, error: null] | [value: null, error: E];\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<This extends AnyAsyncResult, Else>(\n\t\tthis: This,\n\t\tonFailure: (error: InferError<This>) => Else,\n\t) {\n\t\tconst result = (await this) as Result<Value, InferError<This>>;\n\t\treturn result.getOrElse(onFailure) as Promise<\n\t\t\tInferValue<This> | (Else extends AnyPromise ? Awaited<Else> : Else)\n\t\t>;\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<This extends AnyAsyncResult, SuccessResult, FailureResult>(\n\t\tthis: This,\n\t\tonSuccess: (value: InferValue<This>) => SuccessResult,\n\t\tonFailure: (error: InferError<This>) => FailureResult,\n\t) {\n\t\tconst result = (await this) as Result<InferValue<This>, InferError<This>>;\n\t\treturn result.fold(onSuccess, onFailure) as Promise<\n\t\t\t| (SuccessResult extends AnyPromise\n\t\t\t\t\t? Awaited<SuccessResult>\n\t\t\t\t\t: SuccessResult)\n\t\t\t| (FailureResult extends AnyPromise\n\t\t\t\t\t? Awaited<FailureResult>\n\t\t\t\t\t: 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<This extends AnyAsyncResult>(\n\t\tthis: This,\n\t\taction: (error: InferError<This>) => void | Promise<void>,\n\t): AsyncResult<InferValue<This>, InferError<This>> {\n\t\treturn new AsyncResult<InferValue<This>, InferError<This>>(\n\t\t\t(resolve, reject) =>\n\t\t\t\tthis.then(async (result) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!result.ok) {\n\t\t\t\t\t\t\tawait action(result.error as InferError<This>);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve(result as OuterResult<InferValue<This>, InferError<This>>);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\treject(e);\n\t\t\t\t\t}\n\t\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<This extends AnyAsyncResult>(\n\t\tthis: This,\n\t\taction: (value: InferValue<This>) => void | Promise<void>,\n\t): AsyncResult<InferValue<This>, InferError<This>> {\n\t\treturn new AsyncResult<InferValue<This>, InferError<This>>(\n\t\t\t(resolve, reject) =>\n\t\t\t\tthis.then(async (result) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (result.ok) {\n\t\t\t\t\t\t\tawait action(result.value as InferValue<This>);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve(result as OuterResult<InferValue<This>, InferError<This>>);\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}).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 be a generator function or a function that\n\t * returns other {@link Result} or {@link AsyncResult} instances, which will be returned\n\t * as-is (the `Error` types will be merged). Conceptually, it is similar to `Array.flatMap`.\n\t * This map operation will be ignored if the current result represents a failure.\n\t *\n\t * @param transform callback function to transform the value of the result. The callback can be async or a generator function 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\t * @example\n\t * using a generator function to transform the value\n\t * ```ts\n\t * function* doubleValue(value: number) {\n\t * return value * 2;\n\t * }\n\t *\n\t * declare const result: AsyncResult<number, Error>;\n\t * const transformed = result.map(doubleValue); // AsyncResult<number, Error>\n\t * ```\n\t */\n\tmap<This extends AnyAsyncResult, ReturnType, U = Awaited<ReturnType>>(\n\t\tthis: This,\n\t\ttransform: (value: InferValue<This>) => ReturnType,\n\t) {\n\t\treturn new AsyncResult<any, any>((resolve, reject) => {\n\t\t\tthis.then(async (result) => resolve(await result.map(transform))).catch(\n\t\t\t\treject,\n\t\t\t);\n\t\t}) as [InferValue<This>] extends [never]\n\t\t\t? AsyncResult<InferValue<This>, InferError<This>>\n\t\t\t: [ReturnType] extends [Generator | AsyncGenerator]\n\t\t\t\t? AsyncResult<\n\t\t\t\t\t\tInferGeneratorReturn<ReturnType>,\n\t\t\t\t\t\tInferGeneratorError<ReturnType> | InferError<This>\n\t\t\t\t\t>\n\t\t\t\t: [ReturnType] extends [Promise<infer PValue>]\n\t\t\t\t\t? PValue extends U\n\t\t\t\t\t\t? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>>\n\t\t\t\t\t\t: never\n\t\t\t\t\t: ReturnType extends U\n\t\t\t\t\t\t? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>>\n\t\t\t\t\t\t: never;\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 or a generator function 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<\n\t\tThis extends AnyAsyncResult,\n\t\tReturnType,\n\t\tErrorType = NativeError,\n\t\tU = Awaited<ReturnType>,\n\t>(\n\t\tthis: This,\n\t\ttransformValue: (value: InferValue<This>) => 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) => resolve(result as AnyOuterResult))\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\tResultFactory.error(\n\t\t\t\t\t\t\t\ttransformError ? transformError(error) : error,\n\t\t\t\t\t\t\t),\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 [InferValue<This>] extends [never]\n\t\t\t? AsyncResult<InferValue<This>, InferError<This>>\n\t\t\t: [ReturnType] extends [Generator | AsyncGenerator]\n\t\t\t\t? AsyncResult<\n\t\t\t\t\t\tInferGeneratorReturn<ReturnType>,\n\t\t\t\t\t\tInferGeneratorError<ReturnType> | InferError<This> | ErrorType\n\t\t\t\t\t>\n\t\t\t\t: [ReturnType] extends [Promise<infer PValue>]\n\t\t\t\t\t? PValue extends U\n\t\t\t\t\t\t? AsyncResult<\n\t\t\t\t\t\t\t\tExtractValue<U>,\n\t\t\t\t\t\t\t\tInferError<This> | ExtractError<U> | ErrorType\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t: never\n\t\t\t\t\t: ReturnType extends U\n\t\t\t\t\t\t? AsyncResult<\n\t\t\t\t\t\t\t\tExtractValue<U>,\n\t\t\t\t\t\t\t\tInferError<This> | ExtractError<U> | ErrorType\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t: never;\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<This extends AnyAsyncResult, NewError>(\n\t\tthis: This,\n\t\ttransform: (error: InferError<This>) => NewError,\n\t) {\n\t\treturn new AsyncResult<InferValue<This>, NewError>((resolve, reject) =>\n\t\t\tthis.then(async (result) => {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tresult.mapError(transform) as OuterResult<\n\t\t\t\t\t\t\tInferValue<This>,\n\t\t\t\t\t\t\tNewError\n\t\t\t\t\t\t>,\n\t\t\t\t\t);\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 be a generator function or a function that\n\t * returns other {@link Result} or {@link AsyncResult} instances, which will be returned as-is (much like Array.flatMap).\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 or a generator function 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<This extends AnyAsyncResult, ReturnType, U = Awaited<ReturnType>>(\n\t\tthis: This,\n\t\tonFailure: (error: InferError<This>) => ReturnType,\n\t) {\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 as any);\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 [InferError<This>] extends [never]\n\t\t\t? AsyncResult<InferValue<This>, InferError<This>>\n\t\t\t: [ReturnType] extends [Generator | AsyncGenerator]\n\t\t\t\t? AsyncResult<\n\t\t\t\t\t\tInferGeneratorReturn<ReturnType> | InferValue<This>,\n\t\t\t\t\t\tInferGeneratorError<ReturnType>\n\t\t\t\t\t>\n\t\t\t\t: [ReturnType] extends [Promise<infer PValue>]\n\t\t\t\t\t? PValue extends U\n\t\t\t\t\t\t? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>>\n\t\t\t\t\t\t: never\n\t\t\t\t\t: ReturnType extends U\n\t\t\t\t\t\t? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>>\n\t\t\t\t\t\t: 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 or a generator function as well.\n\t * @param transformError callback function to transform any potential caught error while recovering the result.\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<\n\t\tThis extends AnyAsyncResult,\n\t\tReturnType,\n\t\tErrorType = NativeError,\n\t\tU = Awaited<ReturnType>,\n\t>(\n\t\tthis: This,\n\t\tonFailure: (error: InferError<This>) => ReturnType,\n\t\ttransformError?: (error: unknown) => ErrorType,\n\t) {\n\t\treturn new AsyncResult<any, any>((resolve, reject) =>\n\t\t\tthis.then((result) => {\n\t\t\t\tresolve(result.recoverCatching(onFailure, transformError) as any);\n\t\t\t}).catch(reject),\n\t\t) as [InferError<This>] extends [never]\n\t\t\t? AsyncResult<InferValue<This>, InferError<This>>\n\t\t\t: [ReturnType] extends [Generator | AsyncGenerator]\n\t\t\t\t? AsyncResult<\n\t\t\t\t\t\tInferGeneratorReturn<ReturnType> | InferValue<This>,\n\t\t\t\t\t\tInferGeneratorError<ReturnType> | ErrorType\n\t\t\t\t\t>\n\t\t\t\t: [ReturnType] extends [Promise<infer PValue>]\n\t\t\t\t\t? PValue extends U\n\t\t\t\t\t\t? AsyncResult<\n\t\t\t\t\t\t\t\tInferValue<This> | ExtractValue<U>,\n\t\t\t\t\t\t\t\tExtractError<U> | ErrorType\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t: never\n\t\t\t\t\t: ReturnType extends U\n\t\t\t\t\t\t? AsyncResult<\n\t\t\t\t\t\t\t\tInferValue<This> | ExtractValue<U>,\n\t\t\t\t\t\t\t\tExtractError<U> | ErrorType\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t: never;\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) =>\n\t\t\tresolve(ResultFactory.error(error) as OuterResult<never, Error>),\n\t\t);\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) =>\n\t\t\tresolve(ResultFactory.ok(value) as OuterResult<Value, never>),\n\t\t);\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(\n\t\t\t\t\t\tResultFactory.isResult(value) ? value : ResultFactory.ok(value),\n\t\t\t\t\t),\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, reject) => {\n\t\t\tpromise\n\t\t\t\t.then((value) =>\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tResultFactory.isResult(value) ? value : ResultFactory.ok(value),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.catch((caughtError) => {\n\t\t\t\t\tresolve(ResultFactory.error(transform?.(caughtError) ?? caughtError));\n\t\t\t\t})\n\t\t\t\t.catch(reject);\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\tconstructor(\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*[Symbol.iterator](): Generator<{ error: Err; async: false }, Value, any> {\n\t\treturn yield this as any;\n\t}\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.ok} 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.ok) {\n\t * result.value; // number\n\t * }\n\t * ```\n\t */\n\tget value() {\n\t\treturn this._value as ValueOr<Value, Err, undefined>;\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.ok} 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.ok) {\n\t * result.error; // Error\n\t * }\n\t * ```\n\t */\n\tget error() {\n\t\treturn this._error as ErrorOr<Value, Err, undefined>;\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\tget ok() {\n\t\treturn this.success as [Err] extends [never] ? true : false;\n\t}\n\n\t/**\n\t * @deprecated use {@linkcode Result.ok} instead.\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 [Value] extends [never] ? never : OuterResult.Ok<Value> {\n\t\treturn this.success;\n\t}\n\n\t/**\n\t * @deprecated use {@linkcode Result.ok} instead.\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 [Err] extends [never] ? never : OuterResult.Error<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<T extends AnyResult, V = InferValue<T>, E = InferError<T>>(this: T) {\n\t\treturn [this._value ?? null, this._error ?? null] as [E] extends [never]\n\t\t\t? [value: V, error: never]\n\t\t\t: [V] extends [never]\n\t\t\t\t? [value: never, error: E]\n\t\t\t\t: [value: V, error: null] | [value: null, error: E];\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<This extends AnyResult, Else>(\n\t\tthis: This,\n\t\tonFailure: (error: InferError<This>) => Else,\n\t): Else extends Promise<infer U> ? Promise<Value | U> : Value | Else {\n\t\tif (isAsyncFn(onFailure)) {\n\t\t\treturn (\n\t\t\t\tthis.success ? Promise.resolve(this._value) : onFailure(this._error)\n\t\t\t) 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<This extends AnyResult, SuccessResult, FailureResult>(\n\t\tthis: This,\n\t\tonSuccess: (value: InferValue<This>) => SuccessResult,\n\t\tonFailure: (error: InferError<This>) => 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 as InferValue<This>)\n\t\t\t: onFailure(this._error as InferError<This>);\n\n\t\treturn (\n\t\t\tisAsync && !isPromise(outcome) ? Promise.resolve(outcome) : outcome\n\t\t) as Contains<SuccessResult | FailureResult, AnyPromise> extends true\n\t\t\t? Promise<Awaited<SuccessResult> | Awaited<FailureResult>>\n\t\t\t: SuccessResult | FailureResult;\n\t}\n\n\t/**\n\t * Allows you to effectively match the errors of a failed result using the returned instance of the {@link Matcher} class.\n\t * Note: this method can only be called on a failed result, otherwise it will return `undefined`. You can narrow the result\n\t * by checking the `ok` property.\n\t *\n\t * @returns {@link Matcher} instance that can be used to build a chain of matching patterns for the errors of the result.\n\t *\n\t * @example\n\t * Matching against error classes\n\t * ```ts\n\t * declare const result: Result<number, NotFoundError | UserDeactivatedError>;\n\t *\n\t * if (!result.ok) {\n\t * return result\n\t * .match()\n\t * \t\t .when(NotFoundError, (error) => console.error(\"User not found\", error))\n\t * \t\t .when(UserDeactivatedError, (error) => console.error(\"User is deactivated\", error))\n\t * .run()\n\t * }\n\t * ```\n\t *\n\t * @example\n\t * Matching against string constants with an else clause\n\t * ```ts\n\t * declare const result: Result<number, \"not-found\" | \"user-deactivated\">;\n\t * if (!result.ok) {\n\t * return result\n\t * .match()\n\t * \t\t .when(\"not-found\", (error) => console.error(\"User not found\", error))\n\t * .else((error) => console.error(\"Unknown error\", error))\n\t * .run()\n\t * }\n\t * ```\n\t */\n\tmatch<This extends AnyResult>(this: This) {\n\t\treturn (this.failure ? new Matcher(this._error) : undefined) as [\n\t\t\tInferValue<This>,\n\t\t] extends [never]\n\t\t\t? Matcher<InferError<This>>\n\t\t\t: \"'match()' can only be called on a failed result. Please narrow the result by checking the 'ok' property.\";\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<This extends AnyResult, ReturnValue>(\n\t\tthis: This,\n\t\taction: (error: Err) => ReturnValue,\n\t): ReturnValue extends AnyPromise\n\t\t? AsyncResult<InferValue<This>, InferError<This>>\n\t\t: OuterResult<InferValue<This>, InferError<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(ResultFactory.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<This extends AnyResult>(\n\t\tthis: This,\n\t\taction: (value: InferValue<This>) => Promise<void>,\n\t): AsyncResult<InferValue<This>, InferError<This>>;\n\tonSuccess<This extends AnyResult>(\n\t\tthis: This,\n\t\taction: (value: InferValue<This>) => void,\n\t): OuterResult<InferValue<This>, InferError<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(() =>\n\t\t\t\t\t\tresolve(ResultFactory.ok(this._value)),\n\t\t\t\t\t);\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}