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 };