UNPKG

typescript-result

Version:

A Result type inspired by Rust and Kotlin that leverages TypeScript's powerful type system to simplify error handling and make your code more readable and maintainable.

971 lines (968 loc) 46.6 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 IsPromiseOrAsyncFunction<T> = T extends AnyAsyncFunction ? true : T extends Promise<any> ? true : false; type IsFunction<T> = T extends AnyFunction ? true : false; type IsPromise<T> = T extends AnyPromise ? true : false; type UnionContainsPromise<Union> = AnyPromise extends Union ? true : false; type ListContains<Items extends any[]> = Items[number] extends false ? false : true; type ListContainsPromiseOrAsyncFunction<T extends any[]> = ListContains<{ [Index in keyof T]: IsPromiseOrAsyncFunction<T[Index]>; }>; type ListContainsFunction<T extends any[]> = ListContains<{ [Index in keyof T]: IsFunction<T[Index]>; }>; type ListContainsPromise<T extends any[]> = ListContains<{ [Index in keyof T]: IsPromise<T[Index]>; }>; type Union<T extends any[]> = T[number]; type Unwrap<T> = T extends (...args: any[]) => Promise<infer U> ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T; type UnwrapList<Items extends any[]> = { [Index in keyof Items]: Unwrap<Items[Index]>; }; type InferPromise<T> = T extends Promise<infer U> ? U : never; type AnyPromise = Promise<any>; type AnyFunction<Returning = any> = (...args: any[]) => Returning; type AnyAsyncFunction<Returning = any> = (...args: any[]) => Promise<Returning>; type NativeError = globalThis.Error; type AnyValue = {}; type InferError<T> = T extends Result<any, infer Error> ? Error : never; type InferValue<T> = T extends Result<infer Value, any> ? Value : T; type InferErrors<Items extends any[]> = { [Index in keyof Items]: InferError<Items[Index]>; }; type InferValues<Items extends any[]> = { [Index in keyof Items]: InferValue<Items[Index]>; }; type AnyResult = Result<any, any>; type AnyAsyncResult = AsyncResult<any, any>; 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 AccountForFunctionThrowing<Items extends any[]> = ListContainsFunction<Items> extends true ? NativeError : ListContainsPromise<Items> extends true ? NativeError : never; /** * 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; /** * 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(): Promise<[ Err ] extends [never] ? [value: Value, error: never] : [Value] extends [never] ? [value: never, error: Err] : [value: Value, error: null] | [value: null, error: Err]>; /** * @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<Else>(onFailure: (error: Err) => Else): Promise<Value | Unwrap<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<SuccessResult, FailureResult>(onSuccess: (value: Value) => SuccessResult, onFailure: (error: Err) => FailureResult): Promise<Unwrap<SuccessResult> | Unwrap<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(action: (error: Err) => void | Promise<void>): AsyncResult<Value, Err>; /** * 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(action: (value: Value) => void | Promise<void>): AsyncResult<Value, Err>; /** * Transforms the value of a successful result using the {@link transform} callback. * The {@link transform} callback can also return other {@link Result} or {@link AsyncResult} instances, * which will be returned as-is (the `Error` types will be merged). * The operation will be ignored if the result represents a failure. * * @param transform callback function to transform the value of the result. The callback can be async 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> * ``` */ map<ReturnType>(transform: (value: Value) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError> : AsyncResult<PromiseValue, Err> : ReturnType extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError> : AsyncResult<ReturnType, Err>; /** * 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 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<ReturnType, ErrorType = NativeError>(transformValue: (value: Value) => ReturnType, transformError?: (error: unknown) => ErrorType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError | ErrorType> : AsyncResult<PromiseValue, Err | ErrorType> : ReturnType extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError | ErrorType> : AsyncResult<ReturnType, Err | ErrorType>; /** * 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<NewError>(transform: (error: Err) => NewError): AsyncResult<Value, 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 return other {@link Result} or {@link AsyncResult} instances, * which will be returned as-is. * 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 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<ReturnType>(onFailure: (error: Err) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError> : AsyncResult<PromiseValue | Value, never> : ReturnType extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError> : AsyncResult<Value | ReturnType, 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 as well. * @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance * if it represents a success. */ recoverCatching<ReturnType>(onFailure: (error: Err) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError | NativeError> : AsyncResult<PromiseValue | Value, NativeError> : ReturnType extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError | NativeError> : AsyncResult<Value | ReturnType, NativeError>; /** * Print-friendly representation of the `AsyncResult` instance. */ toString(): string; } /** * Represents the outcome of an operation that can either succeed or fail. */ declare class Result<Value, Err> { private readonly _value; private readonly _error; private constructor(); /** * 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; /** * 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.isOk} 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.isOk()) { * 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.isError} 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.isError()) { * result.error; // Error * } * ``` */ get error(): ErrorOr<Value, Err, undefined>; private get success(); private get failure(); /** * 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 Result<[Value] extends [never] ? AnyValue : Value, never>; /** * 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 Result<never, [Err] extends [never] ? AnyValue : 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(): [Err] extends [never] ? [value: Value, error: never] : [Value] extends [never] ? [value: never, error: Err] : [value: Value, error: null] | [value: null, error: Err]; /** * @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<Else>(onFailure: (error: Err) => 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<SuccessResult, FailureResult>(onSuccess: (value: Value) => SuccessResult, onFailure: (error: Err) => FailureResult): UnionContainsPromise<SuccessResult | FailureResult> extends true ? Promise<Unwrap<SuccessResult> | Unwrap<FailureResult>> : SuccessResult | 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: Result<number, Error>; * * result * .onFailure((error) => console.error("I'm failing!", error)) * .map((value) => value * 2); // proceed with other operations * ``` */ onFailure<ReturnValue>(action: (error: Err) => ReturnValue): ReturnValue extends AnyPromise ? AsyncResult<Value, Err> : 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(action: (value: Value) => Promise<void>): AsyncResult<Value, Err>; onSuccess(action: (value: Value) => void): this; /** * Transforms the value of a successful result using the {@link transform} callback. * The {@link transform} callback can also return other {@link Result} or {@link AsyncResult} instances, * which will be returned as-is (the `Error` types will be merged). * The operation will be ignored if the result represents a failure. * * @param transform callback function to transform the value of the result. The callback can be async 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> * ``` */ map<ReturnType>(transform: (value: Value) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError> : AsyncResult<PromiseValue, Err> : ReturnType extends Result<infer ResultValue, infer ResultError> ? Result<ResultValue, Err | ResultError> : Result<ReturnType, Err>; /** * 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 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<ReturnType, ErrorType = NativeError>(transformValue: (value: Value) => ReturnType, transformError?: (err: unknown) => ErrorType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<ResultValue, Err | ResultError | ErrorType> : AsyncResult<PromiseValue, Err | ErrorType> : ReturnType extends Result<infer ResultValue, infer ResultError> ? Result<ResultValue, Err | ResultError | ErrorType> : Result<ReturnType, Err | ErrorType>; /** * 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<NewError>(transform: (error: Err) => NewError): Result<Value, 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 return other {@link Result} or {@link AsyncResult} instances, * which will be returned as-is. * 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 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<ReturnType>(onFailure: (error: Err) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError> : AsyncResult<PromiseValue | Value, never> : ReturnType extends Result<infer ResultValue, infer ResultError> ? Result<Value | ResultValue, ResultError> : Result<Value | ReturnType, 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 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. */ recoverCatching<ReturnType>(onFailure: (error: Err) => ReturnType): ReturnType extends Promise<infer PromiseValue> ? PromiseValue extends Result<infer ResultValue, infer ResultError> ? AsyncResult<Value | ResultValue, ResultError | NativeError> : AsyncResult<PromiseValue | Value, NativeError> : ReturnType extends Result<infer ResultValue, infer ResultError> ? Result<Value | ResultValue, ResultError | NativeError> : Result<Value | ReturnType, NativeError>; /** * Returns a string representation of the result. */ toString(): string; /** * 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<number, never> * ``` */ static ok(): Result<void, never>; static ok<Value>(value: Value): Result<Value, never>; /** * 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<never, NotFoundError> * ``` */ static error<Error>(error: Error): Result<never, Error>; /** * 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 AnyResult; /** * 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; private static run; private static allInternal; /** * 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, or {@linkcode Promise}. * @returns combined result of all the operations. * * > [!NOTE] * > Any exceptions that might be thrown are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode Result.allCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * basic usage * ```ts * declare function createTask(name: string): Result<Task, IOError>; * * const tasks = ["task-a", "task-b", "task-c"]; * const result = Result.all(...tasks.map(createTask)); // Result<Task[], IOError> * ``` * * @example * running multiple operations and combining the results * ```ts * const result = Result.all( * "a", * Promise.resolve("b"), * Result.ok("c"), * Result.try(async () => "d"), * () => "e", * () => Result.try(async () => "f"), * () => Result.ok("g"), * async () => "h", * ); // AsyncResult<[string, string, string, string, string, string, string, string], Error> * ``` */ static all<Items extends any[], Unwrapped extends any[] = UnwrapList<Items>>(...items: Items): ListContainsPromiseOrAsyncFunction<Items> extends true ? AsyncResult<InferValues<Unwrapped>, Union<InferErrors<Unwrapped>>> : Result<InferValues<Unwrapped>, Union<InferErrors<Unwrapped>>>; /** * Similar to {@linkcode Result.all}, but catches any exceptions that might be thrown during the operations. * @param items one or multiple literal value, function, {@linkcode Result} or {@linkcode AsyncResult} instance, or {@linkcode Promise}. * @returns combined result of all the operations. */ static allCatching<Items extends any[], Unwrapped extends any[] = UnwrapList<Items>>(...items: Items): ListContainsPromiseOrAsyncFunction<Items> extends true ? AsyncResult<InferValues<Unwrapped>, Union<InferErrors<Unwrapped>> | AccountForFunctionThrowing<Items>> : Result<InferValues<Unwrapped>, Union<InferErrors<Unwrapped>> | AccountForFunctionThrowing<Items>>; /** * Wraps a function and returns a new function that returns a result. Especially useful when you want to work with * external functions that might throw exceptions. * The returned function will catch any exceptions that might be thrown and encapsulate them in a failed result. * * @param fn function to wrap. Can be synchronous or asynchronous. * @returns a new function that returns a result. * * @example * basic usage * ```ts * declare function divide(a: number, b: number): number; * * const safeDivide = Result.wrap(divide); * const result = safeDivide(10, 0); // Result<number, Error> * ``` */ static wrap<Fn extends AnyAsyncFunction>(fn: Fn): (...args: Parameters<Fn>) => AsyncResult<InferPromise<ReturnType<Fn>>, NativeError>; static wrap<Fn extends AnyFunction>(fn: Fn): (...args: Parameters<Fn>) => Result<ReturnType<Fn>, NativeError>; /** * Executes the given {@linkcode fn} function and encapsulates the returned value as a successful result, or the * thrown exception as a failed result. In a way, you can view this method as a try-catch block that returns a result. * * @param fn function with code to execute. Can be synchronous or asynchronous. * @param transform optional callback to transform the caught error into a more meaningful error. * @returns a new {@linkcode Result} instance. * * @example * basic usage * ```ts * declare function saveFileToDisk(filename: string): void; // might throw an error * * const result = Result.try(() => saveFileToDisk("file.txt")); // Result<void, Error> * ``` * * @example * basic usage with error transformation * ```ts * declare function saveFileToDisk(filename: string): void; // might throw an error * * const result = Result.try( * () => saveFileToDisk("file.txt"), * (error) => new IOError("Failed to save file", { cause: error }) * ); // Result<void, IOError> * ``` */ static try<Fn extends AnyAsyncFunction<AnyResult>, R = InferPromise<ReturnType<Fn>>>(fn: Fn): AsyncResult<InferValue<R>, InferError<R> | NativeError>; static try<Fn extends AnyFunction<AnyResult>, R = ReturnType<Fn>>(fn: Fn): Result<InferValue<R>, InferError<R> | NativeError>; static try<ReturnType extends AnyPromise>(fn: () => ReturnType): AsyncResult<InferPromise<ReturnType>, NativeError>; static try<ReturnType>(fn: () => ReturnType): Result<ReturnType, NativeError>; static try<ReturnType extends AnyPromise, ErrorType extends AnyValue>(fn: () => ReturnType, transform: (error: unknown) => ErrorType): AsyncResult<InferPromise<ReturnType>, ErrorType>; static try<ReturnType, ErrorType extends AnyValue>(fn: () => ReturnType, transform: (error: unknown) => ErrorType): Result<ReturnType, ErrorType>; /** * Utility method to transform a Promise, that holds a literal value or * a {@linkcode Result} or {@linkcode AsyncResult} instance, into an {@linkcode AsyncResult} instance. Useful when you want to immediately chain operations * after calling an async function. * * @param value a Promise that holds a literal value or a {@linkcode Result} or {@linkcode AsyncResult} instance. * * @returns a new {@linkcode AsyncResult} instance. * * > [!NOTE] * > Any exceptions that might be thrown are not caught, so it is your responsibility * > to handle these exceptions. Please refer to {@linkcode Result.fromAsyncCatching} for a version that catches exceptions * > and encapsulates them in a failed result. * * @example * basic usage * * ```ts * declare function someAsyncOperation(): Promise<Result<number, Error>>; * * // without 'Result.fromAsync' * const result = (await someAsyncOperation()).map((value) => value * 2); // Result<number, Error> * * // with 'Result.fromAsync' * const asyncResult = Result.fromAsync(someAsyncOperation()).map((value) => value * 2); // AsyncResult<number, Error> * ``` */ static fromAsync<T extends Promise<AnyAsyncResult>>(value: T): T extends Promise<AsyncResult<infer V, infer E>> ? AsyncResult<V, E> : never; static fromAsync<T extends Promise<AnyResult>>(value: T): T extends Promise<Result<infer V, infer E>> ? AsyncResult<V, E> : never; static fromAsync<T extends AnyPromise>(value: T): T extends Promise<infer V> ? AsyncResult<V, never> : never; /** * Similar to {@linkcode Result.fromAsync} this method transforms a Promise into an {@linkcode AsyncResult} instance. * In addition, it catches any exceptions that might be thrown during the operation and encapsulates them in a failed result. */ static fromAsyncCatching<T extends Promise<AnyAsyncResult>>(value: T): T extends Promise<AsyncResult<infer V, infer E>> ? AsyncResult<V, E | NativeError> : never; static fromAsyncCatching<T extends Promise<AnyResult>>(value: T): T extends Promise<Result<infer V, infer E>> ? AsyncResult<V, E | NativeError> : never; static fromAsyncCatching<T extends AnyPromise>(value: T): T extends Promise<infer V> ? AsyncResult<V, NativeError> : never; /** * Asserts that the provided result is successful. If the result is a failure, an error is thrown. * Useful in unit tests. * * @param result the result instance to assert against. */ static assertOk<Value>(result: Result<Value, any>): asserts result is Result<Value, never>; /** * Asserts that the provided result is a failure. If the result is successful, an error is thrown. * Useful in unit tests. * * @param result the result instance to assert against. */ static assertError<Err>(result: Result<any, Err>): asserts result is Result<never, Err>; } export { AsyncResult, Result, assertUnreachable };