📖

@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に格納されます。safetrueを指定した場合は省略可能です。
  • safe: 任意。trueを指定すると「try関数では例外は投げられない」と仮定します。この場合catchは不要となり、エラーが起きた場合はthrowで大域脱出されます。

戻り値

(…args) => Resultという関数を返します。生成される関数の引数は元のtry関数の引数と同じです。戻り値のResult型は、try関数が返した値を成功値とし、エラー時はcatchで返した値がエラー値となります。tryasync関数を渡した場合は自動的に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.mapErrorFailureのエラーメッセージ文字列を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(成功でも失敗でも可)を返す関数を指定します。fnPromiseを含むResultAsyncを返してもOKです。

戻り値

Resultをチェインする関数を返します。与えたResultが成功ならfnを実行してそのResultを返し、失敗なら何もせず元のFailureを返す動作になります。結果の型はfnが返すResultの型に従います(元のResultfnResultが非同期なら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)を表現するのに便利で、Promisethenに相当する働きをします。


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)をそのまま返す点が異なります。fnFailureを返した場合のみ、結果は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を返します。(元のResultfnResultが非同期なら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を受け取ります。

戻り値

成功値を返します。引数のResultSuccessならその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.unwrapErrorunwrap同様に失敗側に決め打ちした処理です。あえて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であればResultSuccess<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.filterisSuccessを使えばResultの配列から成功結果だけを抽出することもできます。


Result.isFailure

isFailure<E>(result: Result.Result<unknown, E>): result is Failure<E>

与えられたResultが失敗(Failure)かどうかを判定する型ガード関数です。使い方や効果はResult.isSuccessの逆で、trueであればResultFailure型(errorを持つ)だと特定できます。

引数

  • result: 任意のResultを受け取ります。

戻り値

result is Failure<T>つまりブール値(true/false)を返します。trueであればResultFailure<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モジュールの関数群(上記のmapandThen等)と組み合わせて書くことで、ネストの無い読みやすいエラー処理チェインを構築できます。

引数

最初に任意の値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/byethrowResultモジュールに含まれる関数を全て紹介しました。まだ足りていない機能はあるので今後も追加していく予定です。issueによる機能要望などもお待ちしております。

最後に、Result型を使うかどうか判断する際は「そのエラーは業務上ハンドリングすべきものか?」という基準が有用です。そうしたエラーにはResultを、そうでないものは従来通りthrowで処理し、双方を適材適所で使い分けることで、堅牢で見通しの良いエラーハンドリングを実現できます。ぜひ@praha/byethrowをプロジェクトに導入し、快適なエラー処理の手法を試してみてください。

PrAha

Discussion