#Validation and Recovery
The andThrough and orThrough functions allow you to run additional computations for validation or recovery while preserving the original result value.
#andThrough - Validation Without Transformation
The andThrough function runs an additional computation using the success value, but returns the original result if the additional computation is successful. If either the original result or the side effect result is a Failure, that failure is returned.
This is useful for running validations or side effects without altering the main result on success.
#Basic Usage
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<5, "Must be > 0"> result = import Result Result .const pipe: <Result.Result<5, never>, Result.Result<5, "Must be > 0">>(a: Result.Result<5, never>, ab: (a: Result.Result<5, never>) => Result.Result<5, "Must be > 0">) => Result.Result<5, "Must be > 0"> (+25 overloads) pipe (
import Result Result .const succeed: <5>(value: 5) => Result.Result<5, never> (+1 overload) succeed (5),
import Result Result .const andThrough: <Result.Result<5, never>, Result.Success<void> | Result.Failure<"Must be > 0">>(fn: (a: 5) => Result.Success<void> | Result.Failure<"Must be > 0">) => (result: Result.Result<5, never>) => Result.Result<5, "Must be > 0"> (+1 overload) andThrough ((value: 5 value ) => {
return 0 < value: 5 value ? import Result Result .const succeed: () => Result.ResultFor<never, void, never> (+1 overload) succeed () : import Result Result .const fail: <"Must be > 0">(error: "Must be > 0") => Result.Result<never, "Must be > 0"> (+1 overload) fail ('Must be > 0');
}),
);
// { type: 'Success', value: 5 } - original value is preserved#When the Input is a Failure
If the input is a Failure, andThrough does nothing and returns the Failure as-is:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, "error" | "Must be > 0"> result = import Result Result .const pipe: <Result.Result<never, "error">, Result.Result<never, "error" | "Must be > 0">>(a: Result.Result<never, "error">, ab: (a: Result.Result<never, "error">) => Result.Result<never, "error" | "Must be > 0">) => Result.Result<never, "error" | "Must be > 0"> (+25 overloads) pipe (
import Result Result .const fail: <"error">(error: "error") => Result.Result<never, "error"> (+1 overload) fail ('error'),
import Result Result .const andThrough: <Result.Result<never, "error">, Result.Success<void> | Result.Failure<"Must be > 0">>(fn: (a: never) => Result.Success<void> | Result.Failure<"Must be > 0">) => (result: Result.Result<never, "error">) => Result.Result<never, "error" | "Must be > 0"> (+1 overload) andThrough ((value: never value ) => {
return 0 < value: never value ? import Result Result .const succeed: () => Result.ResultFor<never, void, never> (+1 overload) succeed () : import Result Result .const fail: <"Must be > 0">(error: "Must be > 0") => Result.Result<never, "Must be > 0"> (+1 overload) fail ('Must be > 0');
}),
);
// { type: 'Failure', error: 'error' }#When the Function Returns a Failure
If the side effect returns a Failure, that failure is returned instead:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<-10, "Must be > 0"> result = import Result Result .const pipe: <Result.Result<-10, never>, Result.Result<-10, "Must be > 0">>(a: Result.Result<-10, never>, ab: (a: Result.Result<-10, never>) => Result.Result<-10, "Must be > 0">) => Result.Result<-10, "Must be > 0"> (+25 overloads) pipe (
import Result Result .const succeed: <-10>(value: -10) => Result.Result<-10, never> (+1 overload) succeed (-10),
import Result Result .const andThrough: <Result.Result<-10, never>, Result.Success<void> | Result.Failure<"Must be > 0">>(fn: (a: -10) => Result.Success<void> | Result.Failure<"Must be > 0">) => (result: Result.Result<-10, never>) => Result.Result<-10, "Must be > 0"> (+1 overload) andThrough ((value: -10 value ) => {
return 0 < value: -10 value ? import Result Result .const succeed: () => Result.ResultFor<never, void, never> (+1 overload) succeed () : import Result Result .const fail: <"Must be > 0">(error: "Must be > 0") => Result.Result<never, "Must be > 0"> (+1 overload) fail ('Must be > 0');
}),
);
// { type: 'Failure', error: 'Must be > 0' }#Example: Multiple Validations
A common use case is running multiple validations on a value:
import { import Result Result } from '@praha/byethrow';
type type User = {
name: string;
age: number;
email: string;
}
User = { name: string name : string; age: number age : number; email: string email : string };
declare const const validateName: (user: User) => Result.Result<void, "Invalid name"> validateName : (user: User user : type User = {
name: string;
age: number;
email: string;
}
User ) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'Invalid name'>;
declare const const validateAge: (user: User) => Result.Result<void, "Invalid age"> validateAge : (user: User user : type User = {
name: string;
age: number;
email: string;
}
User ) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'Invalid age'>;
declare const const validateEmail: (user: User) => Result.Result<void, "Invalid email"> validateEmail : (user: User user : type User = {
name: string;
age: number;
email: string;
}
User ) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'Invalid email'>;
const const validateUser: (user: User) => Result.Result<User, "Invalid name" | "Invalid age" | "Invalid email"> validateUser = (user: User user : type User = {
name: string;
age: number;
email: string;
}
User ) => import Result Result .const pipe: <Result.Result<User, never>, Result.Result<User, "Invalid name">, Result.Result<User, "Invalid name" | "Invalid age">, Result.Result<User, "Invalid name" | "Invalid age" | "Invalid email">>(a: Result.Result<User, never>, ab: (a: Result.Result<User, never>) => Result.Result<User, "Invalid name">, bc: (b: Result.Result<User, "Invalid name">) => Result.Result<User, "Invalid name" | "Invalid age">, cd: (c: Result.Result<...>) => Result.Result<...>) => Result.Result<...> (+25 overloads) pipe (
import Result Result .const succeed: <User>(value: User) => Result.Result<User, never> (+1 overload) succeed (user: User user ),
import Result Result .const andThrough: <Result.Result<User, never>, Result.Result<void, "Invalid name">>(fn: (a: User) => Result.Result<void, "Invalid name">) => (result: Result.Result<User, never>) => Result.Result<User, "Invalid name"> (+1 overload) andThrough (const validateName: (user: User) => Result.Result<void, "Invalid name"> validateName ),
import Result Result .const andThrough: <Result.Result<User, "Invalid name">, Result.Result<void, "Invalid age">>(fn: (a: User) => Result.Result<void, "Invalid age">) => (result: Result.Result<User, "Invalid name">) => Result.Result<User, "Invalid name" | "Invalid age"> (+1 overload) andThrough (const validateAge: (user: User) => Result.Result<void, "Invalid age"> validateAge ),
import Result Result .const andThrough: <Result.Result<User, "Invalid name" | "Invalid age">, Result.Result<void, "Invalid email">>(fn: (a: User) => Result.Result<void, "Invalid email">) => (result: Result.Result<User, "Invalid name" | "Invalid age">) => Result.Result<User, "Invalid name" | "Invalid age" | "Invalid email"> (+1 overload) andThrough (const validateEmail: (user: User) => Result.Result<void, "Invalid email"> validateEmail ),
);
// Returns the original user if all validations pass, otherwise the first error#orThrough - Recovery with Original Failure
The orThrough function runs an additional computation to attempt recovery from a failure, but returns the original failure if the recovery is successful.
If the original result is a Success, it is returned immediately without running the function. If the original result is a Failure, the function is executed with the error value. If the function returns a Success, the original failure is returned. If the function returns a Failure, that new failure is returned.
This is useful for attempting to recover from a failed operation (e.g., cleanup, rollback) while still preserving the original error.
#Basic Usage
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<5, never> result = import Result Result .const pipe: <Result.Result<5, never>, Result.Result<5, never>>(a: Result.Result<5, never>, ab: (a: Result.Result<5, never>) => Result.Result<5, never>) => Result.Result<5, never> (+25 overloads) pipe (
import Result Result .const succeed: <5>(value: 5) => Result.Result<5, never> (+1 overload) succeed (5),
import Result Result .const orThrough: <Result.Result<5, never>, Result.Result<void, never>>(fn: (a: never) => Result.Result<void, never>) => (result: Result.Result<5, never>) => Result.Result<5, never> (+1 overload) orThrough ((error: never error ) => {
// failure recovery logic here
return import Result Result .const succeed: () => Result.ResultFor<never, void, never> (+1 overload) succeed ();
}),
);
// { type: 'Success', value: 5 } - success passes through unchanged#When the Input is a Failure
If the input is a Failure, orThrough runs the function but preserves the original error:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, "error"> result = import Result Result .const pipe: <Result.Result<never, "error">, Result.Result<never, "error">>(a: Result.Result<never, "error">, ab: (a: Result.Result<never, "error">) => Result.Result<never, "error">) => Result.Result<never, "error"> (+25 overloads) pipe (
import Result Result .const fail: <"error">(error: "error") => Result.Result<never, "error"> (+1 overload) fail ('error'),
import Result Result .const orThrough: <Result.Result<never, "error">, Result.Result<void, never>>(fn: (a: "error") => Result.Result<void, never>) => (result: Result.Result<never, "error">) => Result.Result<never, "error"> (+1 overload) orThrough ((error: "error" error ) => {
// failure recovery logic here
return import Result Result .const succeed: () => Result.ResultFor<never, void, never> (+1 overload) succeed ();
}),
);
// Logs 'Logging error: error' and returns { type: 'Failure', error: 'error' }#When the Function Returns a Failure
If the function returns a Failure, that new failure replaces the original:
import { import Result Result } from '@praha/byethrow';
const const result: Result.Result<never, "original error" | "new error"> result = import Result Result .const pipe: <Result.Result<never, "original error">, Result.Result<never, "original error" | "new error">>(a: Result.Result<never, "original error">, ab: (a: Result.Result<never, "original error">) => Result.Result<never, "original error" | "new error">) => Result.Result<never, "original error" | "new error"> (+25 overloads) pipe (
import Result Result .const fail: <"original error">(error: "original error") => Result.Result<never, "original error"> (+1 overload) fail ('original error'),
import Result Result .const orThrough: <Result.Result<never, "original error">, Result.Result<never, "new error">>(fn: (a: "original error") => Result.Result<never, "new error">) => (result: Result.Result<never, "original error">) => Result.Result<never, "original error" | "new error"> (+1 overload) orThrough ((error: "original error" error ) => {
return import Result Result .const fail: <"new error">(error: "new error") => Result.Result<never, "new error"> (+1 overload) fail ('new error');
}),
);
// { type: 'Failure', error: 'new error' }#Example: Recovery with Cleanup
A common use case is attempting to recover from a failed operation while performing cleanup:
import { import Result Result } from '@praha/byethrow';
declare const const uploadFile: (path: string, content: string) => Result.Result<void, "UploadFileFailed"> uploadFile : (path: string path : string, content: string content : string) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'UploadFileFailed'>;
declare const const savePost: (id: string) => Result.Result<void, "PostCreateFailed"> savePost : (id: string id : string) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'PostCreateFailed'>;
declare const const deleteFile: (path: string) => Result.Result<void, "DeleteFileFailed"> deleteFile : (path: string path : string) => import Result Result .type Result<T, E> = Result.Success<T> | Result.Failure<E>A union type representing either a success or a failure.
@typeParamT - The type of the Success value.@typeParamE - The type of the Failure value.@exampleimport { Result } from '@praha/byethrow';
const doSomething = (): Result.Result<number, string> => {
return Math.random() > 0.5
? { type: 'Success', value: 10 }
: { type: 'Failure', error: 'Oops' };
};
@categoryCore Types Result <void, 'DeleteFileFailed'>;
const const createPost: (id: string, path: string, content: string) => Result.Result<void, "UploadFileFailed" | "PostCreateFailed" | "DeleteFileFailed"> createPost = (id: string id : string, path: string path : string, content: string content : string) => import Result Result .const pipe: <Result.Result<void, "UploadFileFailed">, Result.Result<void, "UploadFileFailed" | "PostCreateFailed">, Result.Result<void, "UploadFileFailed" | "PostCreateFailed" | "DeleteFileFailed">>(a: Result.Result<void, "UploadFileFailed">, ab: (a: Result.Result<void, "UploadFileFailed">) => Result.Result<void, "UploadFileFailed" | "PostCreateFailed">, bc: (b: Result.Result<void, "UploadFileFailed" | "PostCreateFailed">) => Result.Result<void, "UploadFileFailed" | "PostCreateFailed" | "DeleteFileFailed">) => Result.Result<...> (+25 overloads) pipe (
const uploadFile: (path: string, content: string) => Result.Result<void, "UploadFileFailed"> uploadFile (`/posts/${id: string id }`, content: string content ),
import Result Result .const andThen: <Result.Result<void, "UploadFileFailed">, Result.Result<void, "PostCreateFailed">>(fn: (a: void) => Result.Result<void, "PostCreateFailed">) => (result: Result.Result<void, "UploadFileFailed">) => Result.Result<void, "UploadFileFailed" | "PostCreateFailed"> (+1 overload) andThen (() => const savePost: (id: string) => Result.Result<void, "PostCreateFailed"> savePost (id: string id )),
import Result Result .const orThrough: <Result.Result<void, "UploadFileFailed" | "PostCreateFailed">, Result.Result<void, "DeleteFileFailed">>(fn: (a: "UploadFileFailed" | "PostCreateFailed") => Result.Result<void, "DeleteFileFailed">) => (result: Result.Result<void, "UploadFileFailed" | "PostCreateFailed">) => Result.Result<void, "UploadFileFailed" | "PostCreateFailed" | "DeleteFileFailed"> (+1 overload) orThrough (() => const deleteFile: (path: string) => Result.Result<void, "DeleteFileFailed"> deleteFile (`/posts/${id: string id }`)),
);
// If createPost fails, deleteFile is called to clean up
// but the original PostCreateFailed is returned#References
| Function | Purpose |
|---|---|
| andThrough(fn) | Run validation while preserving the original value |
| orThrough(fn) | Attempt recovery while preserving the original error |
