@praha/byethrowの全機能リファレンス
はじめに
前回の記事で@praha/byethrow
について紹介しましたが、本記事ではこのライブラリの全機能についてより詳しく紹介したいと思います。
@praha/byethrow
は、JavaScript/TypeScript向けの軽量でシンプルなエラーハンドリングライブラリです。Result
型を用いることで、関数の成功と失敗を明示的に表現し、try/catch
に頼らない型安全なエラーハンドリングを実現できます。
以下では、Result
モジュールに含まれる全ての関数について、用途 (何をする関数か)、引数, 戻り値, 使用例コードの順で解説します。まだこのライブラリを知らない方でも分かりやすいよう、具体的なコード例を交えて説明します。
APIリファレンス
Result.succeed
succeed(): Result.Result<void, never>
succeed<T>(value: T): ResultFor<T, Awaited<T>, never>
与えた値を成功結果(Success)として包みます。入力がPromise
であれば自動的に非同期のResultAsync
へラップされます。
引数
-
value
: 任意の値を受け取ります。省略した場合はundefined相当の成功(void型)となります。
戻り値
成功を表すResult
オブジェクトを返します。型はResult<T, never>
(エラー型は存在しない)です。Promiseを渡した場合は自動的にResultAsync<T, never>
になります。
使用例
import { Result } from '@praha/byethrow';
// 基本的な使い方(同期)
const result1 = Result.succeed(42);
// result1の型: Result.Result<number, never>
// result1の値: { type: 'Success', value: 42 }
// Promiseを渡す(非同期Result)
const result2 = Result.succeed(Promise.resolve(42));
// result2の型: Result.ResultAsync<number, never>
// Promiseをunwrapする
const result3 = await result2;
// result3の型: Result.Result<number, never>
// 引数なしで呼ぶ(voidを成功として返す)
const result4 = Result.succeed();
// result4の型: Result.Result<void, never>
Result.fail
fail(): Result.Result<never, void>
fail<E>(error: E): ResultFor<E, never, Awaited<E>>
エラー値を失敗結果(Failure)として包みます。こちらも入力がPromise
であれば非同期ResultAsync
として扱われます。
引数
-
error
: エラーを表す任意の値を受け取ります。省略した場合はundefined(型はvoid)をエラー値とします。(ほとんど出番はありませんが対称性のための動作です)
戻り値
失敗を表すResult
オブジェクトを返します。型はResult<never, E>
(成功値型は存在しない)です。Promise
を渡した場合はResultAsync<never, E>
になります。
使用例
import { Result } from '@praha/byethrow';
// 基本的な使い方(同期エラー)
const result1 = Result.fail('Something went wrong');
// result1の型: Result.Result<never, string>
// result1の値: { type: 'Failure', error: 'Something went wrong' }
// Promiseをエラーとして包む(非同期Result)
const result2 = Result.fail(Promise.resolve('Async error'));
// result2の型: Result.ResultAsync<never, string>
// Promiseをunwrapする
const result3 = awat result2;
// result3の型: Result.Result<never, string>
// 引数なしで呼ぶ(エラー値なしのFailure)
const result4 = Result.fail();
// result4の型: Result.Result<never, void>
Result.do
do(): Result.Result<{}, never>
Result.do()
は空の成功オブジェクトを生成する関数です。内部的にはResult.succeed({})
のエイリアスで、主に処理パイプラインの初期値として使われます。
例えば「まず空の成功から出発し、後続のandThenで逐次処理を積み上げていく」ようなケースで重宝します。
引数
引数はありません。
戻り値
{}
(空オブジェクト)を成功値とするResult
を返します。型はResult<{}, never>
です。
使用例
import { Result } from '@praha/byethrow';
// 空の成功を作り、そこに処理を積み上げる例
const result = Result.pipe(
Result.do(),
Result.bind('message', () => Result.succeed('初期処理完了')),
Result.map((result) => result.message),
);
// resultの値: { type: 'Success', value: { message: '初期処理完了' } }
このようにResult.do()
で空のSuccess
からスタートすれば、Result.bind
などと組み合わせて複数の結果をマージしていくような複雑なパイプラインを組み立てることができます(詳細は後述のResult.bind
の項を参照)。
Result.try
try<T extends (...args: readonly any[]) => Promise<any>, E>(
options: { catch: (error: unknown) => E; try: T },
): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, E>
try<T extends (...args: readonly any[]) => Promise<any>>(
options: { safe: true; try: T },
): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, never>
try<T extends (...args: readonly any[]) => any, E>(
options: { catch: (error: unknown) => E; try: T },
): (...args: Parameters<T>) => Result.Result<ReturnType<T>, E>
try<T extends (...args: readonly any[]) => any>(
options: { safe: true; try: T },
): (...args: Parameters<T>) => Result.Result<ReturnType<T>, never>
例外を投げうる関数をResult
でラップするユーティリティです。Result.try
に関数を渡すことで、その関数の実行をtry-catch
で囲み、エラー発生時にはFailure
として結果を返す新しい関数を生成します。同期関数・非同期関数のどちらにも対応しており、後述するsafe
オプションを使うと「この関数はエラーを投げない」と宣言することもできます。
引数
一つのオブジェクトを受け取ります。オブジェクトのプロパティは以下の通りです。
-
try
: 実行したい関数本体を指定します。この関数内でエラーがthrow
されると捕捉されます(async関数も可)。 -
catch
: エラー発生時に呼ばれるハンドラ関数を指定します。引数には捕捉されたerror
(型はunknown)が渡され、この関数の戻り値がFailure
に格納されます。safe
にtrue
を指定した場合は省略可能です。 -
safe
: 任意。true
を指定すると「try関数では例外は投げられない」と仮定します。この場合catchは不要となり、エラーが起きた場合はthrow
で大域脱出されます。
戻り値
(…args) => Result
という関数を返します。生成される関数の引数は元のtry
関数の引数と同じです。戻り値のResult
型は、try
関数が返した値を成功値とし、エラー時はcatch
で返した値がエラー値となります。try
にasync
関数を渡した場合は自動的にPromise
を含むResultAsync
になります。
使用例
import { Result } from '@praha/byethrow';
// 例1: 同期関数の例外処理
const safeDivide = Result.try({
try: (a: number, b: number) => {
if (b === 0) throw new Error('Divide by zero!');
return a / b;
},
catch: (err) => `計算エラー: ${err}` // エラーを文字列メッセージに変換
});
const result1 = safeDivide(6, 3);
// result1: { type: 'Success', value: 2 }
const result2 = safeDivide(6, 0);
// result1: { type: 'Failure', error: '計算エラー: Error: Divide by zero!' }
// 例2: safeオプション(エラーを投げない関数)
const addOneSafe = Result.try({
safe: true,
try: (x: number) => x + 1
});
const result3 = addOneSafe(5);
// result3: { type: 'Success', value: 6 } // エラー型はnever
// 例3: 非同期関数の例外処理
const fetchData = Result.try({
try: async (id: string) => {
const res = await fetch(`/api/data/${id}`);
if (!res.ok) {
throw new Error('Not Found');
}
return res;
},
catch: (e) => new Error(`Fetch失敗: ${e}`)
});
const result4 = await fetchData('abc');
// result4: Result.ResultAsync<Response, Error>
Result.try
を使うことで、関数内の例外発生をすべてResult
で受け取り扱うことができます。上記例では、0除算エラーやHTTPエラーをResult
内に収め、呼び出し側でエラー内容の参照が可能になっています。safe: true
の場合はエラー型がnever
となり、成功しか起こり得ないResult
となるため、エラー処理を省略できます。
Result.map
map<R1 extends ResultMaybeAsync<any, any>, T2>(
fn: (a: InferSuccess<R1>) => T2,
): (result: R1) => ResultFor<R1, T2, InferFailure<R1>>
map<T1, T2>(
fn: (a: T1) => T2,
): <R1 extends ResultMaybeAsync<T1, any>>(
result: R1,
) => ResultFor<R1, T2, InferFailure<R1>>
Result
が成功(Success)の場合に、その成功値を別の値に変換する関数です。入力が失敗(Failure)であれば何もせずそのまま返します。要するに、「成功時の値に対するArray.map
のようなもの」です。
引数
-
fn
: 変換関数を受け取ります。成功値を受け取り、新しい値を返す関数を指定します。
戻り値
新しい成功値を持つResult
を返す関数を返します。Result.map
自体はカリー化されており、通常はResult.pipe
などで利用します。直接使う場合、Result.map(fn)(result)
といった呼び出しも可能です。戻り値のResult
型は元の成功値がU
型に変換されたものになります。
使用例
import { Result } from '@praha/byethrow';
const result1 = Result.pipe(
Result.succeed(2),
Result.map(x => x * 10)
);
// result1: { type: 'Success', value: 20 }
const result2 = Result.pipe(
Result.fail('error'),
Result.map(x => x * 10)
);
// result2: { type: 'Failure', error: 'error' } // Failureはそのまま
上記のように、Result.map
は成功値2を受け取り*10
した20
を新たな成功値として返しています。失敗の場合はfn
は実行されず、エラー内容も変わりません。Result.map
は同期Result
と非同期ResultAsync
の両方に使えます。ResultAsync
の場合はfn
内でPromise
を返す必要はなく、通常時と同じく普通に値変換を書くことで内部で非同期チェーン上に適用されます。
Result.mapError
mapError<R1 extends ResultMaybeAsync<any, any>, E2>(
fn: (a: InferFailure<R1>) => E2,
): (result: R1) => ResultFor<R1, InferSuccess<R1>, E2>
mapError<E1, E2>(
fn: (a: E1) => E2,
): <R1 extends ResultMaybeAsync<any, E1>>(
result: R1,
) => ResultFor<R1, InferSuccess<R1>, E2>
Result.map
のエラー版です。Result
が失敗(Failure)の場合に、エラー値を別の値に変換します。成功(Success)の場合は何もせずそのまま返します。主にエラー情報の型変換やメッセージ加工に使います。
引数
-
fn
: 変換関数を受け取ります。元のエラーを受け取り、新しいエラーを返す関数です。
戻り値
新しいエラー値を持つResult
を返す関数を返します。成功の場合は入力そのまま、失敗の場合はエラー型がF
に変換されたResult
となります。
使用例
import { Result } from '@praha/byethrow';
const result1 = Result.pipe(
Result.fail('NotFound'),
Result.mapError(msg => new Error(`コード:${msg}`))
);
// result1: { type: 'Failure', error: Error('コード:NotFound') }
const result2 = Result.pipe(
Result.succeed(123),
Result.mapError(msg => new Error(`コード:${msg}`))
);
// result2: { type: 'Success', value: 123 } // Successはそのまま
このように、Result.mapError
でFailure
のエラーメッセージ文字列をError
オブジェクトにラップし直すことができます。成功時には何もしないので、そのまま成功値123
が保たれています。map
同様、ResultAsync
にも同じように使えます。
Result.andThen
andThen<
R1 extends ResultMaybeAsync<any, any>,
R2 extends ResultMaybeAsync<any, any>,
>(
fn: (a: InferSuccess<R1>) => R2,
): (
result: R1,
) => ResultFor<
R1
| R2,
InferSuccess<R2>,
InferFailure<R1> | InferFailure<R2>,
>
andThen<F extends (a: any) => ResultMaybeAsync<any, any>>(
fn: F,
): <R1 extends ResultMaybeAsync<Parameters<F>[0], any>>(
result: R1,
) => ResultFor<
R1
| ReturnType<F>,
InferSuccess<F>,
InferFailure<R1> | InferFailure<F>,
>
Result
が成功(Success)の場合に、次の処理を実行して新たなResult
を返すための関数です(いわゆる成功時のチェイン)。Result.map
との違いは、andThen
のコールバックは通常の値ではなくResult
を返す点です。失敗(Failure)の場合はコールバックを呼ばず、そのままFailure
を伝播します。
引数
-
fn
: チェイン関数を受け取ります。成功値を受け取り、新たなResult
(成功でも失敗でも可)を返す関数を指定します。fn
がPromise
を含むResultAsync
を返してもOKです。
戻り値
Result
をチェインする関数を返します。与えたResult
が成功ならfn
を実行してそのResult
を返し、失敗なら何もせず元のFailure
を返す動作になります。結果の型はfn
が返すResult
の型に従います(元のResult
やfn
のResult
が非同期ならResultAsync
になります)。
使用例
import { Result } from '@praha/byethrow';
// 成功時に次のResultを返す例
const result1 = Result.pipe(
Result.succeed(3),
Result.andThen(x => Result.succeed(x * 2))
);
// result1: { type: 'Success', value: 6 }
// 前段がFailureの場合は何もせずそのFailure
const result2 = Result.pipe(
Result.fail('error'),
Result.andThen(x => Result.succeed(x * 2))
);
// result2: { type: 'Failure', error: 'error' }
// コールバック内でFailureを返した場合
const result3 = Result.pipe(
Result.succeed(3),
Result.andThen(x => Result.fail(`error: ${x}`))
);
// result3: { type: 'Failure', error: 'error: 3' }
Result.andThen
を使うと、前の処理が成功した時だけ次の処理へ進めます。例えば上記では3の倍算結果を次に渡していますが、途中でエラーが起きたら以降の処理はスキップされ、そのエラーが最終結果として伝播しています。このようにandThen
はエラー早期帰脱(Early return)を表現するのに便利で、Promise
のthen
に相当する働きをします。
Result.andThrough
andThrough<
R1 extends ResultMaybeAsync<any, any>,
R2 extends ResultMaybeAsync<any, any>,
>(
fn: (a: InferSuccess<R1>) => R2,
): (
result: R1,
) => ResultFor<
R1
| R2,
InferSuccess<R1>,
InferFailure<R1> | InferFailure<R2>,
>
andThrough<F extends (a: any) => ResultMaybeAsync<any, any>>(
fn: F,
): <R1 extends ResultMaybeAsync<Parameters<F>[0], any>>(
result: R1,
) => ResultFor<
R1
| ReturnType<F>,
InferSuccess<R1>,
InferFailure<R1> | InferFailure<F>,
>
Result
が成功(Success)の場合に副次的な処理を実行しつつ、元の成功結果をそのまま次に渡すための関数です。andThen
と似ていますが、andThrough
ではコールバックの成功結果を無視して元の成功値を保持します。コールバックが失敗(Failure)を返した場合はそのFailure
を代わりに伝播します。主にバリデーションやログ出力など、「本流の値を変えずに追加処理を差し込みたい」場合に有用です。
引数
-
fn
: チェイン関数を受け取ります。成功値を受け取り、何らかのResult
(成功時の値の型は問いません)を返す関数を指定します。
戻り値
Result
を返す関数を返します。基本的な動作はResult.andThen
に近いですが、成功時はfn
の結果を無視し元の入力Result
(Success)をそのまま返す点が異なります。fn
がFailure
を返した場合のみ、結果はFailure
に差し替わります。
使用例
import { Result } from '@praha/byethrow';
// バリデーションにandThroughを使う例
const validatePositive = (n: number) =>
n > 0 ? Result.succeed(null) : Result.fail('値は正数である必要があります');
const result1 = Result.pipe(
Result.succeed(5),
Result.andThrough(validatePositive)
);
// result1: { type: 'Success', value: 5 } // 5は正数なので検証通過
const result2 = Result.pipe(
Result.succeed(-10),
Result.andThrough(validatePositive)
);
// result2: { type: 'Failure', error: '値は正数である必要があります' }
const result3 = Result.pipe(
Result.fail('input error'),
Result.andThrough(validatePositive)
);
// result3: { type: 'Failure', error: 'input error' } // もともとのエラーを維持
上記ではvalidatePositive
関数が成功すれば元の値5がそのまま次に渡り、負の値の場合は検証エラーがFailure
として返っています。andThrough
は副作用をResult
チェインに組み込みたい場合に便利で、成功時は本流の値を変えず、失敗時だけ処理を中断してエラーを返せます。なお、andThrough
も非同期Result
に対してそのまま使用可能で、例えばAPIレスポンスの後処理(ログや認可チェックなど)を挟み込む用途にも使えます。
Result.bind
bind<
N extends string,
R1 extends ResultMaybeAsync<any, any>,
R2 extends ResultMaybeAsync<any, any>,
>(
name: N,
fn: (a: InferSuccess<R1>) => R2,
): (
result: R1,
) => InferSuccess<R1> extends object
? ResultFor<
R1
| R2,
{
[K in string | number | symbol]: K extends keyof InferSuccess<
InferSuccess<R1>,
>
? InferSuccess<InferSuccess<R1>>[K<K>]
: InferSuccess<R2>
},
InferFailure<R1>
| InferFailure<R2>,
>
: unknown
オブジェクトのマージ機能を持つ特殊なチェインです。現在のResult
が成功で持つオブジェクトと、新たに実行するResult
の成功値を指定したキーで結合したオブジェクトとして返します。簡単に言うと、andThen
しつつ成功した結果同士をマージするイメージです。主に複数の処理結果を集約して一つのオブジェクトにまとめたい場合などに有用です。
引数
-
name
: 結合先のキー名を受け取ります。後続のResult
成功値をこのキーに割り当てます。 -
fn
: 新たなResult
を返す関数を受け取ります。成功値を受け取り、何らかのResult
(成功時の値の型は問いません)を返す関数を指定します。
戻り値
Result
を返す関数を返します。与えたResult
が成功ならfn
を実行してそのResult
の成功値を指定されたキーでマージした新しいResult
を返し、失敗なら何もせず元のFailure
を返す動作になります。fn
の結果がFailure
なら、そのFailure
を返します。(元のResult
やfn
のResult
が非同期ならResultAsync
になります)
使用例
import { Result } from '@praha/byethrow';
// ユーザ情報に年齢を付加する例
{
const getUser = () => Result.succeed({ name: 'Alice' });
const getAge = () => Result.succeed(20);
const userWithAge = Result.pipe(
getUser(),
Result.bind('age', (user) => getAge())
);
// userWithAge: { type: 'Success', value: { name: 'Alice', age: 20 } }
}
// Failure伝播の例
{
const getAge = () => Result.fail('error');
const result1 = Result.pipe(
getUser(),
Result.bind('age', user => getAge())
);
// result1: { type: 'Failure', error: 'error' }
const result2 = Result.pipe(
Result.fail('initial error'),
Result.bind('age', user => getAge())
);
// result2: { type: 'Failure', error: 'initial error' }
}
上記のuserWithAge
では、最初のSuccess
オブジェクト{ name: 'Alice' }
と、2つ目のSuccess
値20がマージされ、{ name: 'Alice', age: 20 }
という一つのオブジェクトになっています。Result.bind
はオブジェクトの組み立てを段階的に行うのに適しており、フォーム入力のバリデーション結果を集約したり、複数のAPI結果をまとめたりする際に役立ちます。失敗が発生した場合は途中で即座にFailure
が返され、それ以降の結合処理はスキップされます。
Result.unwrap
unwrap<T>(result: Result.Result<T, unknown>): T
Result
が成功(Success)であることを前提に、その成功値を直接取り出す関数です。もしResult
が失敗(Failure)だった場合は、その中に保持されているエラーをthrow
します。この関数はResult
から通常の値に戻すためのもので、最終的にエラーを例外として扱ってもよいレイヤー(例えばテストコードやスクリプトの終了時など)で使用されます。
引数
-
result
: 任意のResultを受け取ります。
戻り値
成功値を返します。引数のResult
がSuccess
ならそのvalue
をそのまま返します。Failure
だった場合は例外をthrow
して呼び出し元に伝播させます。
使用例
import { Result } from '@praha/byethrow';
// Successの場合
const result1: Result.Result<number, string> = { type: 'Success', value: 100 };
const value = Result.unwrap(result1);
// value === 100
// Failureの場合(例外が投げられる)
const result2: Result.Result<number, string> = { type: 'Failure', error: 'Oops' };
try {
Result.unwrap(result2);
} catch (e) {
console.error(e); // 'Oops'
}
Result.unwrap
は安全ではない操作です。Failure
の場合に例外を発生させてしまうため、あえて例外処理に委ねたい場面でのみ利用すべきです。なお、ResultAsync
(Promiseを含むResult)の場合はunwrap
を呼ぶ前にawait
などで完了を待つ必要があります。
Result.unwrapError
unwrapError<E>(result: Result.Result<unknown, E>): E
Result.unwrap
の逆で、Result
が失敗(Failure)であることを前提にエラー値を直接取り出す関数です。引数のResult
が成功(Success)だった場合は、その成功値をthrow
します。
引数
-
result
: 任意のResultを受け取ります。
戻り値
エラー値を返します。引数がFailure
ならそのerror
を返します。Success
だった場合は成功値を例外としてthrow
します。
使用例
import { Result } from '@praha/byethrow';
// Failureの場合
const result1: Result.Result<number, string> = { type: 'Failure', error: 'Something went wrong' };
const err = Result.unwrapError(result1);
// err === 'Something went wrong'
// Successの場合(例外が投げられる)
const result2: Result.Result<number, string> = { type: 'Success', value: 123 };
try {
Result.unwrapError(result2);
} catch (e) {
console.error(e); // 123
}
Result.unwrapError
もunwrap
同様に失敗側に決め打ちした処理です。あえてSuccess
だった場合に例外が投げられるため、こちらも利用場面は限定されます。
Result.isSuccess
isSuccess<T>(result: Result.Result<T, unknown>): result is Success<T>
与えられたResult
が成功(Success)かどうかを判定する型ガード関数です。if
文やfilter
などと組み合わせることで、TypeScript上でResult
の型を絞り込むことができます。
引数
-
result
: 任意のResultを受け取ります。
戻り値
result is Success<T>
つまりブール値(true/false)を返します。true
であればResult
はSuccess<T>
型とみなされ、そのスコープ内ではresult.value
にアクセスしても型エラーになりません。
使用例
import { Result } from '@praha/byethrow';
const result: Result.Result<number, string> = Result.succeed(10);
if (Result.isSuccess(result)) {
// ここではresultはSuccess<number>型なのでvalueに安全にアクセス可能
console.log(result.value * 2); // 20
} else {
// こちらのブロックではresultはFailure型
console.error(result.error);
}
上記のように、Result.isSuccess(result)
がtrue
の場合、そのResult
は成功時の値を持つ型だとコンパイラに認識されます。これにより、失敗時との処理を明確に分けながら型安全に成功値を扱えます。同様にArray.filter
でisSuccess
を使えばResult
の配列から成功結果だけを抽出することもできます。
Result.isFailure
isFailure<E>(result: Result.Result<unknown, E>): result is Failure<E>
与えられたResult
が失敗(Failure)かどうかを判定する型ガード関数です。使い方や効果はResult.isSuccess
の逆で、true
であればResult
がFailure
型(errorを持つ)だと特定できます。
引数
-
result
: 任意のResultを受け取ります。
戻り値
result is Failure<T>
つまりブール値(true/false)を返します。true
であればResult
はFailure<T>
型とみなされ、そのスコープ内ではresult.error
にアクセスしても型エラーになりません。
使用例
const result: Result.Result<number, string> = Result.fail('読み込み失敗');
if (Result.isFailure(result)) {
// resultはFailure<string>型
console.error(result.error);
} else {
// こちらのブロックではresultはSuccess型
console.log(result.value);
}
isSuccess
と結果は表裏一体で、isFailure(result) === !isSuccess(result)
です。どちらを使っても良いですが、コードの意図に応じて「成功であることを確認」したいならisSuccess
、その逆ならisFailure
と書くと読みやすくなるでしょう。
Result.pipe
pipe<A>(a: A): A
pipe<A, B = never, C = never>(a: A, ab: (a: A) => B, bc: (b: B) => C): C
pipe<A, B = never, C = never, D = never>(
a: A,
ab: (a: A) => B,
bc: (b: B) => C,
cd: (c: C) => D,
): D
...
関数型パイプラインを実現するためのヘルパー関数です。最初の入力値から始めて、以降に渡した複数の関数を左から右へ順に適用した結果を返します。|>
演算子のようなものと考えてください。Result
専用ではなく任意の関数に使えますが、特にResult
モジュールの関数群(上記のmap
やandThen
等)と組み合わせて書くことで、ネストの無い読みやすいエラー処理チェインを構築できます。
引数
最初に任意の値a
をとり、続いて単一引数の関数ab
, bc
, cd
, ...を任意個数受け取ります。それぞれ前の出力を次の入力として順次適用します。最大26個まで関数をチェイン可能です。
戻り値
最後の関数適用後の結果値を返します。途中でResult
を扱っていてもシグニチャ上特別扱いはなく、あくまで値を順伝搬する仕組みです。
使用例
import { Result } from '@praha/byethrow';
// 単純な関数パイプラインの例
const output = Result.pipe(
2,
x => x + 1,
x => x * 3,
x => `結果: ${x}`
);
console.log(output); // "結果: 9"
// Resultと組み合わせた例
const finalResult = Result.pipe(
Result.succeed(2),
Result.map(x => x + 1),
Result.map(x => x * 3),
Result.map(x => `結果: ${x}`)
);
// finalResult: { type: 'Success', value: '結果: 9' }
Result.pipe
を使うことで、ネストの深い処理を避けフラットで直観的なコードが書けます。特にResult
を伴う一連の処理を記述する際に、Result.pipe
により逐次処理の流れが上から下へ読み取れるようになります。
おわりに
以上、@praha/byethrow
のResult
モジュールに含まれる関数を全て紹介しました。まだ足りていない機能はあるので今後も追加していく予定です。issue
による機能要望などもお待ちしております。
最後に、Result
型を使うかどうか判断する際は「そのエラーは業務上ハンドリングすべきものか?」という基準が有用です。そうしたエラーにはResult
を、そうでないものは従来通りthrow
で処理し、双方を適材適所で使い分けることで、堅牢で見通しの良いエラーハンドリングを実現できます。ぜひ@praha/byethrow
をプロジェクトに導入し、快適なエラー処理の手法を試してみてください。
Discussion