try / catch エラー処理と catch のナローイング

最終更新:2026-10-22

try / catch エラー処理と catch のナローイング

TS のエラー処理には2つのつらみがあります。1つは catch の err が厳格モードでは unknown であること(JS はどんな値でも throw できるため、コンパイラはそれが Error だと仮定できません)。ナローイングしなければ .message に触れられません。もう1つは「投げるかもしれない」ことが型のシグネチャに現れないことです。対処はこうです。unknown には instanceof でナローイングし、予期できる失敗には Result パターンへ切り替えて、成否を戻り値の型に直接書き込みます。

グループ設定と実践
種類構文
strict モード不要

ℹ️ 説明 エディタは TypeScript を JavaScript にトランスパイルして実行します(型チェックなし)。型チェック結果は各サンプルの出力を参照してください。

📝 構文

// 1) unknown のナローイング
try {
  risky();
} catch (err: unknown) {
  if (err instanceof Error) console.log(err.message);
  else console.log("unknown throw:", err);
}

// 2) Result パターン:ユニオン型で「成功か失敗か」を表す
type Result<T> = { ok: true; value: T } | { ok: false; error: string };

function attempt<T>(fn: () => T): Result<T> {
  try { return { ok: true, value: fn() }; }
  catch { return { ok: false, error: "failed" }; }
}

⚙️ パラメータ

unknowncatch 変数の型です(厳格モードではこれが既定です)。何でも入れられますが、使う前には必ずナローイングが必要です。これが安全性の源です
instanceof最もよく使われるナローイングの手段です。err instanceof Error の後なら .message/.stack にアクセスでき、独自のエラークラスにも同じく有効です
Result<T>自作の戻り値の型です。{ ok: true; value: T } | { ok: false; error } の形で、呼び出し側はまず ok を判定しなければならず、判定した分岐の中で value か error のどちらか一方だけが手に入ります

戻り値:Result パターンの戻り値の型は判別可能ユニオンです。ok が true のときだけ value があり、false のときだけ error があります。「失敗の可能性」が型に符号化され、呼び出し側は失敗の分岐を処理しないとコンパイルが通りません。

▶ サンプル

下のコードを編集して「試す」を押すと、トランスパイル実行結果を確認できます:

出力:

caught RangeError: not a positive integer: abc
7 0
{"ok":true,"value":42}
failed: invalid input

(トランスパイル後の実行出力。err は instanceof でナローイングした後でなければ .message にアクセスできません。ナローイングせずに直接 err.message と書くと error TS18046 になります)

💡 ポイント

ℹ️ 説明 JS はどんな値でも(数値、文字列、オブジェクト)throw できるため、TS は catch の err を Error ではなく unknown としました。これは useUnknownInCatchVariables(strict に含まれます)がもたらす保護で、存在しないかもしれない .message プロパティへの期待を防ぎます。
💡 ヒント ナローイングの手段は instanceof だけではありません。typeof err === "string"、"message" in err、isApiError(err) のようなカスタム型ガードも使えます。どれにも合致しない場合を処理する最後のフォールバック分岐を書いて、任意の throw の値をカバーしましょう。
⚠️ 注意 Result パターンは呼び出し側のコードを長くします(すべての呼び出し箇所で ok を判定するため)。よくある折衷案は、中核の業務の流れでは例外を続け、境界の層(ユーザー入力の解析、外部 API の呼び出し)では Result に変え、「予期できる失敗」を呼び出し側の明示的な処理に残すことです。
ケースは今後追加予定です。

📚 関連チュートリアル

❓ よくある質問

Q catch の中で直接 err.message を使えないのはなぜですか?
A err の型が unknown だからです(JS は文字列を含めどんな値でも throw できます)。コンパイラは .message が存在することを保証できません。先にナローイングしましょう。err instanceof Error、typeof err === "string"、"message" in err などを使い、その後で安全にアクセスできます。
Q 独自のエラークラスはどうナローイングますか?
A extends Error で自分のクラスを定義し(たとえば ValidationError)、catch の中で err instanceof ValidationError と絞り込めば、独自のフィールド(err.field など)にアクセスできます。同じ try の中に複数のナローイング分岐を置け、具体的なものから一般的なものへ並べます。
Q いつ例外を使い、いつ Result を使うべきですか?
A 例外は「予期しない状況」(バグ、突然のネットワーク断)に向いており、呼び出し側は基本的に処理すべきではありません。Result は「予期できる失敗」(入力の解析、フォームの検証)に向いており、呼び出し側は必ず処理します。型が強制するからです。判断基準は「呼び出し側がこの失敗を受け取った後、意味のある次の一手を持つか」です。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%