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
text/typescript
/**
* 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]
* >