検証とエラーからの回復

andThroughorThrough 関数を使うと、元の結果値を保持しながら、検証や回復のための追加計算を実行できます。

andThrough - 変換なしの検証

andThrough 関数は成功値を使って追加の計算を実行しますが、追加の計算が成功した場合は元の結果を返します。 元の結果または副作用の結果のいずれかが Failure の場合、その失敗が返されます。

これは、成功時にメインの結果を変更せずに、バリデーションや副作用を実行する場合に便利です。

基本的な使い方

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 } - 元の値が保持される

入力が Failure の場合

入力が Failure の場合、andThrough は何もせず、Failure をそのまま返します。

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

関数が Failure を返す場合

副作用が Failure を返すと、その失敗が代わりに返されます。

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

例:複数のバリデーション

よくある使い方として、値に対して複数のバリデーションを実行する例があります。

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
),
); // すべてのバリデーションが通れば元のユーザーを返し、そうでなければ最初のエラーを返す

orThrough - 元の失敗を保持した回復

orThrough 関数は失敗からの回復を試みる追加の計算を実行しますが、回復が成功した場合は元の失敗を返します

元の結果が Success の場合、関数を実行せずに即座に返されます。 元の結果が Failure の場合、エラー値で関数が実行されます。関数が Success を返すと、元の失敗が返されます。 関数が Failure を返すと、その新しい失敗が返されます。

これは、失敗した操作からの回復(例:クリーンアップ、ロールバック)を試みながら、元のエラーを保持する場合に便利です。

基本的な使い方

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
) => {
// 失敗回復ロジックをここに return
import Result
Result
.
const succeed: () => Result.ResultFor<never, void, never> (+1 overload)
succeed
();
}), ); // { type: 'Success', value: 5 } - 成功はそのまま通過

入力が Failure の場合

入力が Failure の場合、orThrough は関数を実行しますが、元のエラーを保持します。

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
) => {
// 失敗回復ロジックをここに return
import Result
Result
.
const succeed: () => Result.ResultFor<never, void, never> (+1 overload)
succeed
();
}), ); // 'Logging error: error' をログに出力し、{ type: 'Failure', error: 'error' } を返す

関数が Failure を返す場合

関数が Failure を返すと、その新しい失敗が元の失敗を置き換えます。

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

例:クリーンアップを伴う回復

よくある使い方として、クリーンアップを行いながら失敗した操作からの回復を試みる例があります。

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
}`)),
); // createPost が失敗すると、クリーンアップのために deleteFile が呼ばれる // しかし元の PostCreateFailed が返される

リファレンス

関数目的
andThrough(fn)元の値を保持しながらバリデーションを実行する
orThrough(fn)元のエラーを保持しながら回復を試みる