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.

991 lines (989 loc) 64.2 kB
/** * Utility function to assert that a case is unreachable * @param value the value which to check for exhaustiveness * * @example * ```ts * declare const value: "a" | "b" | "c"; * * switch (value) { * case "a": * // do something * break; * case "b": * // do something * break; * default: assertUnreachable(value) // TS should complain here * } * * ``` */ declare function assertUnreachable(value: never): never; type AnyPromise = Promise<any>; type Constructor<T> = abstract new (...args: any[]) => T; type AnyFunction<Returning = any> = (...args: any[]) => Returning; type AnyAsyncFunction<Returning = any> = (...args: any[]) => Promise<Returning>; type NativeError = globalThis.Error; type AnyValue = {}; type Contains<T, V, U = T> = (T extends U ? U extends V ? true : false : false) extends false ? false : true; declare class NonExhaustiveError<E> extends Error { readonly error: E; constructor(error: E); } declare class RedundantElseClauseError<T> extends Error { readonly error: T; constructor(error: T); } type ExtractHandledCases<T extends readonly any[]> = { [I in keyof T]: T[I] extends Constructor<infer U> ? U : T[I]; }[number]; type WhenValue<E> = Constructor<Extract<E, object>> | E; declare class Matcher<out E, InferredOutput = never> { private error; private cases; private defaultHandler; /** * Let's you match against one or more error types. * If the error matches one of the provided types, the corresponding handler will be called. * The last argument must be a handler function, and the preceding arguments must be either * the error class (constructor) or a literal value (e.g. string). * * @example * Match using the class of the error: * ```ts * class ErrorA extends Error { * readonly type = "error-a"; * } * * matcher.when(ErrorA, (error) => console.log("Handled ErrorA:", error)); * ``` * * @example * Match on multiple error classes with a single handler: * ```ts * class ErrorA extends Error { * readonly type = "error-a"; * } * * class ErrorB extends Error { * readonly type = "error-b"; * } * * matcher.when(ErrorA, ErrorB, (error) => { * console.log("Handled ErrorA or ErrorB:", error); * }); * ``` * * @example * Match using a literal value: * ```ts * matcher.when("SOME_ERROR_CODE", (error) => console.log("Handled error with code:", error)); * ``` */ when<T extends WhenValue<E>, U extends readonly WhenValue<E>[], R, HandledCases = ExtractHandledCases<[T, ...U]>>(value: T, ...args: [...rest: U, handler: (error: HandledCases) => R]): Matcher<Exclude<E, HandledCases>, InferredOutput | R>; /** * Registers a handler that will be called if no other case matches. * Note: you can only register one `else` handler, otherwise it will throw an error. * Note: TS will complain if you try to register an `else` handler when all error cases * are already handled. * * @example * ```ts * matcher.else((error) => * console.log("Handled any other error:", error); * ); * ``` */ readonly else: [E] extends [never] ? RedundantElseClauseError<"All error cases are already handled"> : <R>(handler: (error: E) => R) => Matcher<never, InferredOutput | R>; /** * Executes the matcher and returns the result of the first matching handler. * If no handler matches, it will call the `else` handler if registered, * or throw a `NonExhaustiveError` if no `else` handler is registered. * * Note: If not all error cases are handled, this will throw a `NonExhaustiveError`, * and TS will complain with a helpful message indicating which cases are not handled. */ readonly run: [E] extends [never] ? () => Contains<InferredOutput, AnyPromise> extends true ? Promise<Awaited<InferredOutput>> : InferredOutput : NonExhaustiveError<E>; } type InferError<T> = T extends AsyncResult<any, infer Error> ? Error : T extends Result$1<any, infer Error> ? Error : never; type InferValue<T> = T extends AsyncResult<infer Value, any> ? Value : T extends Result$1<infer Value, any> ? Value : T; type AnyResult = Result$1<any, any>; type AnyOuterResult = Result<any, any>; type AnyAsyncResult = AsyncResult<any, any>; type ReturningValue<T> = Result$1<T, any> | AsyncResult<T, any> | Promise<ReturningValue<T>> | T; type ReturningError<T> = Result$1<any, T> | AsyncResult<any, T> | Promise<ReturningError<T>>; type ExtractValue<T> = T extends ReturningValue<infer Value> ? Value : T; type ExtractError<T> = T extends ReturningError<infer Error> ? Error : never; type ExtractValues<T extends any[]> = { [I in keyof T]: T[I] extends Generator | AsyncGenerator ? InferGeneratorReturn<T[I]> : ExtractValue<T[I]>; }; type ExtractErrors<T extends any[]> = { [I in keyof T]: T[I] extends Generator | AsyncGenerator ? InferGeneratorError<T[I]> : ExtractError<T[I]>; }; type ReturnsAsync<T> = Contains<T, AnyAsyncResult | AnyPromise | AsyncGenerator>; type IfReturnsAsync<T, Yes, No> = ReturnsAsync<T> extends true ? Yes : No; type ValueOr<Value, Err, Or> = [Err] extends [never] ? [Value] extends [never] ? Or : Value : Value | Or; type ErrorOr<Value, Err, Or> = [Value] extends [never] ? [Err] extends [never] ? Or : Err : Err | Or; type SyncOrAsyncGenerator<Y, R, N> = Generator<Y, R, N> | AsyncGenerator<Y, R, N>; type InferGeneratorReturn<T> = T extends SyncOrAsyncGenerator<any, infer R, any> ? ExtractValue<R> : never; type InferGeneratorError<T> = [T] extends [ SyncOrAsyncGenerator<never, infer R, any> ] ? InferError<R> : T extends SyncOrAsyncGenerator<{ error: infer E; }, infer R, any> ? E | InferError<R> : never; type IsGeneratorAsync<T> = T extends SyncOrAsyncGenerator<infer Info, infer R, any> ? Contains<Info, { async: true; }> extends true ? true : Contains<T, AsyncGenerator<any, any, any>> extends true ? true : Contains<R, AnyAsyncResult> extends true ? true : false : false; type IfGeneratorAsync<T, Yes, No> = IsGeneratorAsync<T> extends true ? Yes : No; type UnwrapList<T extends any[]> = { [I in keyof T]: T[I] extends AnyFunction<infer U> ? U : T[I]; }; type IsAsync<T> = IsGeneratorAsync<T> extends true ? true : T extends AnyPromise ? true : T extends AnyFunction<infer U> ? IsAsync<U> : never; type ListContainsAsync<T extends any[]> = { [I in keyof T]: IsAsync<T[I]>; }[number] extends false ? false : true; type AccountForThrowing<T extends any[]> = { [I in keyof T]: T[I] extends AnyFunction | AnyPromise ? true : false; }[number] extends false ? never : NativeError; /** * Represents the asynchronous outcome of an operation that can either succeed or fail. */ declare class AsyncResult<Value, Err> extends Promise<Result<Value, Err>> { /** * Utility getter to infer the value type of the result. * Note: this getter does not hold any value, it's only used for type inference. */ $inferValue: Value; /** * Utility getter to infer the error type of the result. * Note: this getter does not hold any value, it's only used for type inference. */ $inferError: Err; [Symbol.iterator](): Generator<{ error: Err; async: true; }, Value, any>; /** * Utility getter to check if the current instance is an `AsyncResult`. */ get isAsyncResult(): true; /** * @returns the result in a tuple format where the first element is the value and the second element is the error. * If the result is successful, the error will be `null`. If the result is a failure, the value will be `null`. * * This method is especially useful when you want to destructure the result into a tuple and use TypeScript's narrowing capabilities. * * @example Narrowing down the result type using destructuring * ```ts * declare const result: AsyncResult<number, ErrorA>; * * const [value, error] = await result.toTuple(); * * if (error) { * // error is ErrorA * return; * } * * // value must be a number * ``` */ toTuple<This extends AnyAsyncResult, V = InferValue<This>, E = InferError<This>>(this: This): Promise<[E] extends [never] ? [value: V, error: never] : [V] extends [never] ? [value: never, error: E] : [value: V, error: null] | [value: null, error: E]>; /** * @returns the encapsulated error if the result is a failure, otherwise `null`. */ errorOrNull(): Promise<ErrorOr<Value, Err, null>>; /** * @returns the encapsulated value if the result is successful, otherwise `null`. */ getOrNull(): Promise<ValueOr<Value, Err, null>>; /** * Retrieves the encapsulated value of the result, or a default value if the result is a failure. * * @param defaultValue The value to return if the result is a failure. * * @returns The encapsulated value if the result is successful, otherwise the default value. * * @example * obtaining the value of a result, or a default value * ```ts * declare const result: AsyncResult<number, Error>; * * const value = await result.getOrDefault(0); // number * ``` * * @example * using a different type for the default value * ```ts * declare const result: AsyncResult<number, Error>; * * const value = await result.getOrDefault("default"); // number | string * ``` */ getOrDefault<Else>(defaultValue: Value | Else): Promise<Value | Else>; /** * Retrieves the value of the result, or transforms the error using the {@link onFailure} callback into a value. * * @param onFailure callback function which allows you to transform the error into a value. The callback can be async as well. * @returns either the value if the result is successful, or the transformed error. * * @example * transforming the error into a value * ```ts * declare const result: AsyncResult<number, Error>; * * const value = await result.getOrElse((error) => 0); // number * ``` * * @example * using an async callback * ```ts * const value = await result.getOrElse(async (error) => 0); // number * ``` */ getOrElse<This extends AnyAsyncResult, Else>(this: This, onFailure: (error: InferError<This>) => Else): Promise<InferValue<This> | (Else extends AnyPromise ? Awaited<Else> : Else)>; /** * Retrieves the encapsulated value of the result, or throws an error if the result is a failure. * * @returns The encapsulated value if the result is successful. * * @throws the encapsulated error if the result is a failure. * * @example * obtaining the value of a result, or throwing an error * ```ts * declare const result: AsyncResult<number, Error>; * * const value = await result.getOrThrow(); // number * ``` */ getOrThrow(): Promise<Value>; /** * Returns the result of the {@link onSuccess} callback when the result represents success or * the result of the {@link onFailure} callback when the result represents a failure. * * > [!NOTE] * > Any exceptions that might be thrown inside the callbacks are not caught, so it is your responsibility * > to handle these exceptions * * @param onSuccess callback function to run when the result is successful. The callback can be async as well. * @param onFailure callback function to run when the result is a failure. The callback can be async as well. * @returns the result of the callback that was executed. * * @example * folding a result to a response-like object * * ```ts * declare const result: AsyncResult<User, NotFoundError | UserDeactivatedError>; * * const response = await result.fold( * (user) => ({ status: 200, body: user }), * (error) => { * switch (error.type) { * case "not-found": * return { status: 404, body: "User not found" }; * case "user-deactivated": * return { status: 403, body: "User is deactivated" }; * } * } * ); * ``` */ fold<This extends AnyAsyncResult, SuccessResult, FailureResult>(this: This, onSuccess: (value: InferValue<This>) => SuccessResult, onFailure: (error: InferError<This>) => FailureResult): Promise<(SuccessResult extends AnyPromise ? Awaited<SuccessResult> : SuccessResult) | (FailureResult extends AnyPromise ? Awaited<FailureResult> : FailureResult)>; /** * Calls the {@link action} callback when the result represents a failure. It is meant to be used for * side-effects and the operation does not modify the result itself. * * @param action callback function to run when the result is a failure. The callback can be async as well. * @returns the original instance of the result. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility * > to handle these exceptions * * @example * adding logging between operations * ```ts * declare const result: AsyncResult<number, Error>; * * result * .onFailure((error) => console.error("I'm failing!", error)) * .map((value) => value * 2); // proceed with other operations * ``` */ onFailure<This extends AnyAsyncResult>(this: This, action: (error: InferError<This>) => void | Promise<void>): AsyncResult<InferValue<This>, InferError<This>>; /** * Calls the {@link action} callback when the result represents a success. It is meant to be used for * side-effects and the operation does not modify the result itself. * * @param action callback function to run when the result is successful. The callback can be async as well. * @returns the original instance of the result. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility * > to handle these exceptions * * @example * adding logging between operations * ```ts * declare const result: AsyncResult<number, Error>; * * result * .onSuccess((value) => console.log("I'm a success!", value)) * .map((value) => value * 2); // proceed with other operations * ``` * * @example * using an async callback * ```ts * declare const result: AsyncResultResult<number, Error>; * * const asyncResult = await result.onSuccess(async (value) => someAsyncOperation(value)); * ``` */ onSuccess<This extends AnyAsyncResult>(this: This, action: (value: InferValue<This>) => void | Promise<void>): AsyncResult<InferValue<This>, InferError<This>>; /** * Transforms the value of a successful result using the {@link transform} callback. * The {@link transform} callback can also be a generator function or a function that * returns other {@link Result} or {@link AsyncResult} instances, which will be returned * as-is (the `Error` types will be merged). Conceptually, it is similar to `Array.flatMap`. * This map operation will be ignored if the current result represents a failure. * * @param transform callback function to transform the value of the result. The callback can be async or a generator function as well. * @returns a new {@linkcode AsyncResult} instance with the transformed value * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link transform} callback are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode AsyncResult.mapCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * transforming the value of a result * ```ts * declare const result: AsyncResult<number, Error>; * * const transformed = result.map((value) => value * 2); // AsyncResult<number, Error> * ``` * * @example * returning a result instance * ```ts * declare const result: AsyncResult<number, Error>; * declare function multiplyByTwo(value: number): Result<number, Error>; * * const transformed = result.map((value) => multiplyByTwo(value)); // AsyncResult<number, Error> * ``` * * @example * doing an async transformation * ```ts * declare const result: AsyncResult<number, Error>; * * const transformed = result.map(async (value) => value * 2); // AsyncResult<number, Error> * ``` * * @example * returning an async result instance * * ```ts * declare const result: AsyncResult<number, Error>; * declare function storeValue(value: number): AsyncResult<boolean, Error>; * * const transformed = result.map((value) => storeValue(value)); // AsyncResult<boolean, Error> * ``` * * @example * using a generator function to transform the value * ```ts * function* doubleValue(value: number) { * return value * 2; * } * * declare const result: AsyncResult<number, Error>; * const transformed = result.map(doubleValue); // AsyncResult<number, Error> * ``` */ map<This extends AnyAsyncResult, ReturnType, U = Awaited<ReturnType>>(this: This, transform: (value: InferValue<This>) => ReturnType): [InferValue<This>] extends [never] ? AsyncResult<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? AsyncResult<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>> : never : ReturnType extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>> : never; /** * Like {@linkcode AsyncResult.map} it transforms the value of a successful result using the {@link transformValue} callback. * In addition, it catches any exceptions that might be thrown inside the {@link transformValue} callback and encapsulates them * in a failed result. * * @param transformValue callback function to transform the value of the result. The callback can be async or a generator function as well. * @param transformError callback function to transform any potential caught error while transforming the value. * @returns a new {@linkcode AsyncResult} instance with the transformed value */ mapCatching<This extends AnyAsyncResult, ReturnType, ErrorType = NativeError, U = Awaited<ReturnType>>(this: This, transformValue: (value: InferValue<This>) => ReturnType, transformError?: (error: unknown) => ErrorType): [InferValue<This>] extends [never] ? AsyncResult<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? AsyncResult<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This> | ErrorType> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U> | ErrorType> : never : ReturnType extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U> | ErrorType> : never; /** * Transforms the encapsulated error of a failed result using the {@link transform} callback into a new error. * This can be useful for instance to capture similar or related errors and treat them as a single higher-level error type * @param transform callback function to transform the error of the result. * @returns new {@linkcode AsyncResult} instance with the transformed error. * * @example * transforming the error of a result * ```ts * const result = Result.try(() => fetch("https://example.com")) * .mapCatching((response) => response.json() as Promise<Data>) * .mapError((error) => new FetchDataError("Failed to fetch data", { cause: error })); * // AsyncResult<Data, FetchDataError>; * ``` */ mapError<This extends AnyAsyncResult, NewError>(this: This, transform: (error: InferError<This>) => NewError): AsyncResult<InferValue<This>, NewError>; /** * Transforms a failed result using the {@link onFailure} callback into a successful result. Useful for falling back to * other scenarios when a previous operation fails. * The {@link onFailure} callback can also be a generator function or a function that * returns other {@link Result} or {@link AsyncResult} instances, which will be returned as-is (much like Array.flatMap). * After a recovery, logically, the result can only be a success. Therefore, the error type is set to `never`, unless * the {@link onFailure} callback returns a result-instance with another error type. * * @param onFailure callback function to transform the error of the result. The callback can be async or a generator function as well. * @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance * if it represents a success. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link onFailure} callback are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode AsyncResult.recoverCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * transforming the error into a value * Note: Since we recover after trying to persist in the database, we can assume that the `DbError` has been taken care * of and therefore it has been removed from the final result. * ```ts * declare function persistInDB(item: Item): AsyncResult<Item, DbError>; * declare function persistLocally(item: Item): AsyncResult<Item, IOError>; * * persistInDB(item).recover(() => persistLocally(item)); // AsyncResult<Item, IOError> * ``` */ recover<This extends AnyAsyncResult, ReturnType, U = Awaited<ReturnType>>(this: This, onFailure: (error: InferError<This>) => ReturnType): [InferError<This>] extends [never] ? AsyncResult<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? AsyncResult<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>> : never : ReturnType extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>> : never; /** * Like {@linkcode AsyncResult.recover} it transforms a failed result using the {@link onFailure} callback into a successful result. * In addition, it catches any exceptions that might be thrown inside the {@link onFailure} callback and encapsulates them * in a failed result. * * @param onFailure callback function to transform the error of the result. The callback can be async or a generator function as well. * @param transformError callback function to transform any potential caught error while recovering the result. * @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance * if it represents a success. */ recoverCatching<This extends AnyAsyncResult, ReturnType, ErrorType = NativeError, U = Awaited<ReturnType>>(this: This, onFailure: (error: InferError<This>) => ReturnType, transformError?: (error: unknown) => ErrorType): [InferError<This>] extends [never] ? AsyncResult<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? AsyncResult<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType> | ErrorType> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U> | ErrorType> : never : ReturnType extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U> | ErrorType> : never; /** * Print-friendly representation of the `AsyncResult` instance. */ toString(): string; } /** * Represents the outcome of an operation that can either succeed or fail. */ declare class Result$1<Value, Err> { private readonly _value; private readonly _error; constructor(_value: Value, _error: Err); /** * Utility getter to infer the value type of the result. * Note: this getter does not hold any value, it's only used for type inference. */ $inferValue: Value; /** * Utility getter to infer the error type of the result. * Note: this getter does not hold any value, it's only used for type inference. */ $inferError: Err; [Symbol.iterator](): Generator<{ error: Err; async: false; }, Value, any>; /** * Utility getter that checks if the current instance is a `Result`. */ get isResult(): true; /** * Retrieves the encapsulated value of the result. * * @returns The value if the operation was successful, otherwise `undefined`. * * __Note:__ You can use {@linkcode Result.ok} to narrow down the type to a successful result. * * @example * obtaining the value of a result, without checking if it's successful * ```ts * declare const result: Result<number, Error>; * * result.value; // number | undefined * ``` * * @example * obtaining the value of a result, after checking for success * ```ts * declare const result: Result<number, Error>; * * if (result.ok) { * result.value; // number * } * ``` */ get value(): ValueOr<Value, Err, undefined>; /** * Retrieves the encapsulated error of the result. * * @returns The error if the operation failed, otherwise `undefined`. * * > [!NOTE] * > You can use {@linkcode Result.ok} to narrow down the type to a failed result. * * @example * obtaining the value of a result, without checking if it's a failure * ```ts * declare const result: Result<number, Error>; * * result.error; // Error | undefined * ``` * * @example * obtaining the error of a result, after checking for failure * ```ts * declare const result: Result<number, Error>; * * if (!result.ok) { * result.error; // Error * } * ``` */ get error(): ErrorOr<Value, Err, undefined>; private get success(); private get failure(); get ok(): [Err] extends [never] ? true : false; /** * @deprecated use {@linkcode Result.ok} instead. * Type guard that checks whether the result is successful. * * @returns `true` if the result is successful, otherwise `false`. * * @example * checking if a result is successful * ```ts * declare const result: Result<number, Error>; * * if (result.isOk()) { * result.value; // number * } * ``` */ isOk(): this is [Value] extends [never] ? never : Result.Ok<Value>; /** * @deprecated use {@linkcode Result.ok} instead. * Type guard that checks whether the result is successful. * * @returns `true` if the result represents a failure, otherwise `false`. * * @example * checking if a result represents a failure * ```ts * declare const result: Result<number, Error>; * * if (result.isError()) { * result.error; // Error * } * ``` */ isError(): this is [Err] extends [never] ? never : Result.Error<Err>; /** * @returns the result in a tuple format where the first element is the value and the second element is the error. * If the result is successful, the error will be `null`. If the result is a failure, the value will be `null`. * * This method is especially useful when you want to destructure the result into a tuple and use TypeScript's narrowing capabilities. * * @example Narrowing down the result type using destructuring * ```ts * declare const result: Result<number, ErrorA>; * * const [value, error] = result.toTuple(); * * if (error) { * // error is ErrorA * return; * } * * // value must be a number * ``` */ toTuple<T extends AnyResult, V = InferValue<T>, E = InferError<T>>(this: T): [E] extends [never] ? [value: V, error: never] : [V] extends [never] ? [value: never, error: E] : [value: V, error: null] | [value: null, error: E]; /** * @returns the encapsulated error if the result is a failure, otherwise `null`. */ errorOrNull(): ErrorOr<Value, Err, null>; /** * @returns the encapsulated value if the result is successful, otherwise `null`. */ getOrNull(): ValueOr<Value, Err, null>; /** * Retrieves the value of the result, or a default value if the result is a failure. * * @param defaultValue The value to return if the result is a failure. * * @returns The encapsulated value if the result is successful, otherwise the default value. * * @example * obtaining the value of a result, or a default value * ```ts * declare const result: Result<number, Error>; * * const value = result.getOrDefault(0); // number * ``` * * @example * using a different type for the default value * ```ts * declare const result: Result<number, Error>; * * const value = result.getOrDefault("default"); // number | string * ``` */ getOrDefault<Else>(defaultValue: Else): Value | Else; /** * Retrieves the value of the result, or transforms the error using the {@link onFailure} callback into a value. * * @param onFailure callback function which allows you to transform the error into a value. The callback can be async as well. * @returns either the value if the result is successful, or the transformed error. * * @example * transforming the error into a value * ```ts * declare const result: Result<number, Error>; * * const value = result.getOrElse((error) => 0); // number * ``` * * @example * using an async callback * ```ts * const value = await result.getOrElse(async (error) => 0); // Promise<number> * ``` */ getOrElse<This extends AnyResult, Else>(this: This, onFailure: (error: InferError<This>) => Else): Else extends Promise<infer U> ? Promise<Value | U> : Value | Else; /** * Retrieves the value of the result, or throws an error if the result is a failure. * * @returns The value if the result is successful. * * @throws the encapsulated error if the result is a failure. * * @example * obtaining the value of a result, or throwing an error * ```ts * declare const result: Result<number, Error>; * * const value = result.getOrThrow(); // number * ``` */ getOrThrow(): Value; /** * Returns the result of the {@link onSuccess} callback when the result represents success or * the result of the {@link onFailure} callback when the result represents a failure. * * > [!NOTE] * > Any exceptions that might be thrown inside the callbacks are not caught, so it is your responsibility * > to handle these exceptions * * @param onSuccess callback function to run when the result is successful. The callback can be async as well. * @param onFailure callback function to run when the result is a failure. The callback can be async as well. * @returns the result of the callback that was executed. * * @example * folding a result to a response-like object * * ```ts * declare const result: Result<User, NotFoundError | UserDeactivatedError>; * * const response = result.fold( * (user) => ({ status: 200, body: user }), * (error) => { * switch (error.type) { * case "not-found": * return { status: 404, body: "User not found" }; * case "user-deactivated": * return { status: 403, body: "User is deactivated" }; * } * } * ); * ``` */ fold<This extends AnyResult, SuccessResult, FailureResult>(this: This, onSuccess: (value: InferValue<This>) => SuccessResult, onFailure: (error: InferError<This>) => FailureResult): Contains<SuccessResult | FailureResult, AnyPromise> extends true ? Promise<Awaited<SuccessResult> | Awaited<FailureResult>> : SuccessResult | FailureResult; /** * Allows you to effectively match the errors of a failed result using the returned instance of the {@link Matcher} class. * Note: this method can only be called on a failed result, otherwise it will return `undefined`. You can narrow the result * by checking the `ok` property. * * @returns {@link Matcher} instance that can be used to build a chain of matching patterns for the errors of the result. * * @example * Matching against error classes * ```ts * declare const result: Result<number, NotFoundError | UserDeactivatedError>; * * if (!result.ok) { * return result * .match() * .when(NotFoundError, (error) => console.error("User not found", error)) * .when(UserDeactivatedError, (error) => console.error("User is deactivated", error)) * .run() * } * ``` * * @example * Matching against string constants with an else clause * ```ts * declare const result: Result<number, "not-found" | "user-deactivated">; * if (!result.ok) { * return result * .match() * .when("not-found", (error) => console.error("User not found", error)) * .else((error) => console.error("Unknown error", error)) * .run() * } * ``` */ match<This extends AnyResult>(this: This): [InferValue<This>] extends [never] ? Matcher<InferError<This>> : "'match()' can only be called on a failed result. Please narrow the result by checking the 'ok' property."; /** * Calls the {@link action} callback when the result represents a failure. It is meant to be used for * side-effects and the operation does not modify the result itself. * * @param action callback function to run when the result is a failure. The callback can be async as well. * @returns the original instance of the result. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility * > to handle these exceptions * * @example * adding logging between operations * ```ts * declare const result: Result<number, Error>; * * result * .onFailure((error) => console.error("I'm failing!", error)) * .map((value) => value * 2); // proceed with other operations * ``` */ onFailure<This extends AnyResult, ReturnValue>(this: This, action: (error: Err) => ReturnValue): ReturnValue extends AnyPromise ? AsyncResult<InferValue<This>, InferError<This>> : Result<InferValue<This>, InferError<This>>; /** * Calls the {@link action} callback when the result represents a success. It is meant to be used for * side-effects and the operation does not modify the result itself. * * @param action callback function to run when the result is successful. The callback can be async as well. * @returns the original instance of the result. If the callback is async, it returns a new {@link AsyncResult} instance. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link action} callback are not caught, so it is your responsibility * > to handle these exceptions * * @example * adding logging between operations * ```ts * declare const result: Result<number, Error>; * * result * .onSuccess((value) => console.log("I'm a success!", value)) * .map((value) => value * 2); // proceed with other operations * ``` * * @example * using an async callback * ```ts * declare const result: Result<number, Error>; * * const asyncResult = await result.onSuccess(async (value) => someAsyncOperation(value)); * ``` */ onSuccess<This extends AnyResult>(this: This, action: (value: InferValue<This>) => Promise<void>): AsyncResult<InferValue<This>, InferError<This>>; onSuccess<This extends AnyResult>(this: This, action: (value: InferValue<This>) => void): Result<InferValue<This>, InferError<This>>; /** * Transforms the value of a successful result using the {@link transform} callback. * The {@link transform} callback can also be a generator function or a function that * returns other {@link Result} or {@link AsyncResult} instances, which will be returned * as-is (the `Error` types will be merged). Conceptually, it is similar to `Array.flatMap`. * This map operation will be ignored if the current result represents a failure. * * @param transform callback function to transform the value of the result. The callback can be async or a generator function as well. * @returns a new {@linkcode Result} instance with the transformed value, or a new {@linkcode AsyncResult} instance * if the transform function is async. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link transform} callback are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode Result.mapCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * transforming the value of a result * ```ts * declare const result: Result<number, Error>; * * const transformed = result.map((value) => value * 2); // Result<number, Error> * ``` * * @example * returning a result instance * ```ts * declare const result: Result<number, Error>; * declare function multiplyByTwo(value: number): Result<number, Error>; * * const transformed = result.map((value) => multiplyByTwo(value)); // Result<number, Error> * ``` * * @example * doing an async transformation * ```ts * declare const result: Result<number, Error>; * * const transformed = result.map(async (value) => value * 2); // AsyncResult<number, Error> * ``` * * @example * returning an async result instance * * ```ts * declare const result: Result<number, Error>; * declare function storeValue(value: number): AsyncResult<boolean, Error>; * * const transformed = result.map((value) => storeValue(value)); // AsyncResult<boolean, Error> * ``` * * @example * using a generator function to transform the value * ```ts * function* doubleValue(value: number) { * return value * 2; * } * * declare const result: Result<number, Error>; * const transformed = result.map(doubleValue); // Result<number, Error> * ``` */ map<This extends AnyResult, ReturnType, U = Awaited<ReturnType>>(this: This, transform: (value: InferValue<This>) => ReturnType): [InferValue<This>] extends [never] ? Result<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? IfGeneratorAsync<ReturnType, AsyncResult<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This>>, Result<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This>>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>> : never : IfReturnsAsync<ReturnType, ReturnType extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U>> : never, ReturnType extends U ? Result<ExtractValue<U>, InferError<This> | ExtractError<U>> : never>; /** * Like {@linkcode Result.map} it transforms the value of a successful result using the {@link transformValue} callback. * In addition, it catches any exceptions that might be thrown inside the {@link transformValue} callback and encapsulates them * in a failed result. * * @param transformValue callback function to transform the value of the result. The callback can be async or a generator function as well. * @param transformError callback function to transform any potential caught error while transforming the value. * @returns a new {@linkcode Result} instance with the transformed value, or a new {@linkcode AsyncResult} instance * if the transform function is async. */ mapCatching<This extends AnyResult, ReturnType, ErrorType = NativeError, U = Awaited<ReturnType>>(this: This, transformValue: (value: InferValue<This>) => ReturnType, transformError?: (err: unknown) => ErrorType): [InferValue<This>] extends [never] ? Result<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? IfGeneratorAsync<ReturnType, AsyncResult<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This> | ErrorType>, Result<InferGeneratorReturn<ReturnType>, InferGeneratorError<ReturnType> | InferError<This> | ErrorType>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U> | ErrorType> : never : IfReturnsAsync<ReturnType, ReturnType extends U ? AsyncResult<ExtractValue<U>, InferError<This> | ExtractError<U> | ErrorType> : never, ReturnType extends U ? Result<ExtractValue<U>, InferError<This> | ExtractError<U> | ErrorType> : never>; /** * Transforms the encapsulated error of a failed result using the {@link transform} callback into a new error. * This can be useful for instance to capture similar or related errors and treat them as a single higher-level error type * @param transform callback function to transform the error of the result. * @returns new {@linkcode Result} instance with the transformed error. * * @example * transforming the error of a result * ```ts * declare const result: Result<number, ErrorA>; * * result.mapError((error) => new ErrorB(error.message)); // Result<number, ErrorB> * ``` */ mapError<This extends AnyResult, NewError>(this: This, transform: (error: InferError<This>) => NewError): Result<InferValue<This>, NewError>; /** * Transforms a failed result using the {@link onFailure} callback into a successful result. Useful for falling back to * other scenarios when a previous operation fails. * The {@link onFailure} callback can also be a generator function or a function that * returns other {@link Result} or {@link AsyncResult} instances, which will be returned as-is (much like Array.flatMap). * After a recovery, logically, the result can only be a success. Therefore, the error type is set to `never`, unless * the {@link onFailure} callback returns a result-instance with another error type. * * @param onFailure callback function to transform the error of the result. The callback can be async or a generator function as well. * @returns a new successful {@linkcode Result} instance or a new successful {@linkcode AsyncResult} instance * when the result represents a failure, or the original instance if it represents a success. * * > [!NOTE] * > Any exceptions that might be thrown inside the {@link onFailure} callback are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode Result.recoverCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * transforming the error into a value * Note: Since we recover after trying to persist in the database, we can assume that the `DbError` has been taken care * of and therefore it has been removed from the final result. * ```ts * declare function persistInDB(item: Item): Result<Item, DbError>; * declare function persistLocally(item: Item): Result<Item, IOError>; * * persistInDB(item).recover(() => persistLocally(item)); // Result<Item, IOError> * ``` */ recover<This extends AnyResult, ReturnType, U = Awaited<ReturnType>>(this: This, onFailure: (error: InferError<This>) => ReturnType): [InferError<This>] extends [never] ? Result<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? IfGeneratorAsync<ReturnType, AsyncResult<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType>>, Result<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType>>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>> : never : IfReturnsAsync<ReturnType, ReturnType extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U>> : never, ReturnType extends U ? Result<InferValue<This> | ExtractValue<U>, ExtractError<U>> : never>; /** * Like {@linkcode Result.recover} it transforms a failed result using the {@link onFailure} callback into a successful result. * In addition, it catches any exceptions that might be thrown inside the {@link onFailure} callback and encapsulates them * in a failed result. * * @param onFailure callback function to transform the error of the result. The callback can be async or a generator function as well. * @param transformError callback function to transform any potential caught error while recovering the result. * @returns a new successful {@linkcode Result} instance or a new successful {@linkcode AsyncResult} instance * when the result represents a failure, or the original instance if it represents a success. */ recoverCatching<This extends AnyResult, ReturnType, ErrorType = NativeError, U = Awaited<ReturnType>>(this: This, onFailure: (error: InferError<This>) => ReturnType, transformError?: (err: unknown) => ErrorType): [InferError<This>] extends [never] ? Result<InferValue<This>, InferError<This>> : [ReturnType] extends [Generator | AsyncGenerator] ? IfGeneratorAsync<ReturnType, AsyncResult<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType> | ErrorType>, Result<InferGeneratorReturn<ReturnType> | InferValue<This>, InferGeneratorError<ReturnType> | ErrorType>> : [ReturnType] extends [Promise<infer PValue>] ? PValue extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U> | ErrorType> : never : IfReturnsAsync<ReturnType, ReturnType extends U ? AsyncResult<InferValue<This> | ExtractValue<U>, ExtractError<U> | ErrorType> : never, ReturnType extends U ? Result<InferValue<This> | ExtractValue<U>, ExtractError<U> | ErrorType> : never>; /** * Returns a string representation of the result. */ toString(): string; } declare class ResultFactory { private constructor(); /** * Creates a new result instance that represents a successful outcome. * * @param value The value to encapsulate in the result. * @returns a new {@linkcode Result} instance. * * @example * ```ts * const result = Result.ok(42); // Result.Ok<number> * ``` */ static ok(): Result.Ok<void>; static ok<Value>(value: Value): Result.Ok<Value>; /** * Creates a new result instance that represents a failed outcome. * * @param error The error to encapsulate in the result. * @returns a new {@linkcode Result} instance. * * @example * ```ts * const result = Result.error(new NotFoundError()); // Result.Error<NotFoundError> * ``` */ static error<const Err extends string>(error: Err): Result.Error<Err>; static error<Err>(error: Err): Result.Error<Err>; /** * Type guard that checks whether the provided value is a {@linkcode Result} instance. * * @param possibleResult any value that might be a {@linkcode Result} instance. * @returns true if the provided value is a {@linkcode Result} instance, otherwise false. */ static isResult(possibleResult: unknown): possibleResult is AnyOuterResult; /** * Type guard that checks whether the provided value is a {@linkcode AsyncResult} instance. * * @param possibleAsyncResult any value that might be a {@linkcode AsyncResult} instance. * @returns true if the provided value is a {@linkcode AsyncResult} instance, otherwise false. */ static isAsyncResult(possibleAsyncResult: unknown): possibleAsyncResult is AnyAsyncResult; /** * Similar to {@linkcode Promise.all}, but for results. * Useful when you want to run multiple independent operations and bundle the outcome into a single result. * All possible values of the individual operations are collected into an array. `Result.all` will fail eagerly, * meaning that as soon as any of the operations fail, the entire result will be a failure. * Each argument can be a mixture of literal values, functions, {@linkcode Result} or {@linkcode AsyncResult} instances, or {@linkcode Promise}. * * @param items one or multiple literal value, function, {@linkcode Result} or {@linkcode AsyncResult} instance, {@linkcode Promise}, or (async) generator function. * @returns combined result of all the operations. * * > [!NOTE] * >