• English
  • 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