#検証とエラーからの回復
andThrough と orThrough 関数を使うと、元の結果値を保持しながら、検証や回復のための追加計算を実行できます。
#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.@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 ),
);
// すべてのバリデーションが通れば元のユーザーを返し、そうでなければ最初のエラーを返す#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.@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 }`)),
);
// createPost が失敗すると、クリーンアップのために deleteFile が呼ばれる
// しかし元の PostCreateFailed が返される#リファレンス
| 関数 | 目的 |
|---|---|
| andThrough(fn) | 元の値を保持しながらバリデーションを実行する |
| orThrough(fn) | 元のエラーを保持しながら回復を試みる |
