neverthrow-wrap-exceptions について
neverthrow-wrap-exceptionsは、回復可能な同期・非同期例外を型付きのneverthrow結果に変換するコーディングスキルです。適切なラッパーの選択と、明示的なエラーマッピングを支援します。
主な機能
- 01失敗の形に応じたラッパー選択
- 02エラーの明示的な型変換
- 03既存のResult値の合成
- 04変換目的だけのcatchチェーンの置き換え
- 05例外処理の変更内容の検証
活用シーン
- 01例外を投げる同期パーサーのラップ
- 02拒否されたPromiseをドメインエラーに変換
- 03catchによる場当たり的な変換のリファクタリング
- 04編集したコードのエラー処理を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ code-sherpas/ agent-skills --skill neverthrow-wrap-exceptionsインストール後、Claude Code で/neverthrow-wrap-exceptionsを入力して読み込まれたか確認します。
詳細
Neverthrow による例外のラップ
目標
場当たり的な try/catch ではなく、neverthrow のヘルパーを使って回復可能な例外を捕捉します。
このスキルが対象とするのは例外の捕捉のみです。タスクで公開戻り値のシグネチャも変更する場合は、このスキルと併せて neverthrow-return-types を使用してください。
例外の発生源を特定する
-
現在、失敗がコードに入り込む箇所を特定します。
- 手書きの
try/catch、変換のみに使われている.catch(...)ラッパー、例外を投げることがわかっている API の直接呼び出し、拒否される可能性がある Promise を返す関数を探します。 - サードパーティライブラリ、パーサー、データベースクライアント、ネットワーククライアント、ファイルシステムヘルパー、スキーマバリデーター、シリアライズ処理を確認します。
- 手書きの
-
ラッパーを選ぶ前に、失敗の形を見分けます。
- 値を返す前に処理が例外を投げる可能性がある場合は、同期処理用の方法を使います。
- 処理が Promise を返すものの、その Promise が存在する前に例外を投げる可能性がある場合は、Promise を返す関数用の方法を使います。
- すでに
PromiseLikeの値が手元にある場合は、Promise インスタンス用の方法を使います。
-
すでに
ResultまたはResultAsyncを返す API はラップしないでください。map、mapErr、andThen、asyncAndThen、またはorElseで直接合成します。
ラッパーを選ぶ
-
同期的に例外を投げる関数には、
Result.fromThrowableまたはfromThrowableを使います。Err側の型を明確にするため、必ずエラーマッパーを渡します。
-
返却前に例外を投げる可能性がある、または非同期実行中に失敗する可能性がある Promise 返却関数には、
ResultAsync.fromThrowableを使います。- 関数呼び出し自体が例外を投げる可能性がある場合は、
ResultAsync.fromPromise(fn(...), ...)よりこちらを優先します。
- 関数呼び出し自体が例外を投げる可能性がある場合は、
-
手元にすでに
PromiseLikeの値がある場合は、ResultAsync.fromPromiseまたはfromPromiseを使います。- 拒否された値は、ただちに具体的なエラー型にマッピングします。
-
同じエラーの形が繰り返し現れる場合は、用途を絞ったマッパー関数を再利用します。
unknown、any、汎用的な文字列よりも、安定したドメインエラーを優先します。
原則として try/catch を避ける
-
neverthrowのヘルパーで対応できる場合、新たに手書きのtry/catchブロックを追加しないでください。- 必要に応じて、リスクのある処理を関数に切り出して、その関数をラップします。
-
周囲の構文が本当に必要とする場合に限り、
try/catchを残します。- 例として、
finallyが必要なクリーンアップ処理、例外を捕捉して変換する必要があるフレームワーク境界、ラッパーヘルパーだけでは適切に表現できない言語構文などがあります。
- 例として、
-
try/catchが引き続き必要な場合は、最も狭い境界に置きます。- 捕捉した値をただちに
Err、または必要なフレームワーク固有のレスポンスに変換します。 - 型付けされていない
unknownのまま、捕捉した値をコードベース内に流さないでください。
- 捕捉した値をただちに
実装ルール
-
例外を投げる、または拒否される処理の発生源近くで、一度だけラップします。
- 同じ処理を入れ子にしてラップしないでください。
-
エラーマッピングを明示します。
- ローカルのスタイルが許す場合は、処理名、入力識別子、上流のステータスコードなど、有用なコンテキストを保持するマッパー関数を優先します。
-
neverthrowによってより明確なラッパーを使える場合は、変換のみを行う.catch(...)チェーンを置き換えます。Promise.resolve、Promise.reject、カスタムラッパーオブジェクトを使ってResultAsyncを手動で模倣しないでください。
パターンの例
const parseConfig = Result.fromThrowable(
JSON.parse,
(error) => ({ type: 'ConfigParseError', cause: error }),
)
const fetchUser = ResultAsync.fromThrowable(
apiClient.getUser,
(error) => ({ type: 'UserFetchError', cause: error }),
)
function readBody(): ResultAsync<RequestBody, BodyReadError> {
return ResultAsync.fromPromise(request.json(), toBodyReadError)
}
完了前に検証する
- 新規または編集した失敗の捕捉処理で、該当する箇所に
neverthrowのヘルパーを使っていることを確認します。 - 各ラッパーの選択が実際の失敗の形(同期的な例外、Promise 返却関数、既存の Promise)に合っていることを確認します。
- すべてのエラーマッパーが明示的なエラー型を生成することを確認します。
- 残っている
try/catchブロックが、慣習ではなく実際の制約を理由としていることを確認します。 - 安全で、かつ作業範囲内であれば、テスト、リンティング、型チェックなど、通常のローカル検証を実行します。
結果を報告する
タスクを完了したら、次の内容を記載してください。
- どの例外をスローする操作、または拒否する操作をラップしたか。
- どの
neverthrowヘルパーを使い、その理由は何か。 - 残っている
try/catchブロックがあれば、その理由と、避けられなかった理由。 - 捕捉または拒否された値を、どのように明示的なエラー型にマッピングしたか。