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.@example
import { 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.@example
import { 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.@example
import { 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.@example
import { 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.@example
import { 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.@example
import { 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

FunctionPurpose
andThrough(fn)Run validation while preserving the original value
orThrough(fn)Attempt recovery while preserving the original error