TypeScriptの「Catch変数はunknownであるべき」という真実 ― `useUnknownInCatchVariables` を使いこなす
現場でコードをレビューしていると、いまだに `catch (e: any)` を見かけることがあります。かつてのTypeScript(バージョン4.4未満)では、これがデフォルトであり、ある種「お作法」でした。しかし、今のモダンなTypeScript環境において、その書き方は「地雷」以外の何物でもありません。
今回は、tsconfigの隠れた重要設定である `useUnknownInCatchVariables` について、なぜこれが必須なのか、そしてどうやって「大人のエラーハンドリング」を実装すべきかを解説します。
なぜ `any` から `unknown` に変える必要があるのか?
まず、JavaScriptの仕様に立ち返りましょう。
JavaScriptの `try-catch` 構文において、`throw` されるものは `Error` オブジェクトだけとは限りません。文字列(`throw “Error!”`)、数値、あるいは `null` や `undefined` すら投げることができます。
TypeScriptの旧仕様(`any` の時代)では、`catch (e)` の `e` は自動的に `any` になっていました。これにより、開発者は「`e` はエラーオブジェクトに決まっている」という甘えから、`e.message` に平気でアクセスしていました。しかし、もし投げられたものが文字列や `null` だった場合、実行時にフロントエンドがクラッシュします。
`useUnknownInCatchVariables` を `true` にすると、`catch` 節の変数は強制的に `unknown` 型になります。これは「お前が投げたものが何者か、お前自身で証明してみろ」という、TypeScriptからの愛ある厳しいメッセージなのです。
実践:型安全なエラーハンドリングの書き方
では、実際に現場でどう書くべきか。`unknown` 型に対して「お前はErrorオブジェクトか?」というチェック(Type Guard)を入れるのが正解です。
try {
// 何らかのAPI呼び出しや処理
throw new Error(“意図的なエラー”);
} catch (e: unknown) {
// ここで e.message にアクセスしようとすると、TSがエラーを吐いて止めてくれる
// console.log(e.message); // エラー! ‘e’ は unknown 型です
// 1. まず「オブジェクトであるか」を確認
if (e instanceof Error) {
// ここでは e は Error 型として推論される
console.error(“エラーメッセージ:”, e.message);
} else {
// 2. Errorオブジェクト以外が投げられた場合のフォールバック
console.error(“予期せぬ型のエラー:”, String(e));
}
}
ブラウザの裏側で起きていること
ブラウザのJavaScriptエンジン(V8など)は、`throw` された値をそのまま呼び出し元へ伝播させます。このとき、型情報は完全に剥がれ落ちています。
つまり、`catch` に飛び込んできた時点で、その変数は「何が入っているかわからない箱」です。`any` を使うことは、中身を確認せずに箱を乱暴に開けるようなもの。対して `unknown` を使うことは、「まず中身を確認し、中身に応じた安全な取り出し方をする」という防衛的プログラミングそのものです。
チームで導入するためのTips
もし今、あなたのプロジェクトが `tsconfig` でこの設定を有効にしていないなら、今すぐ `true` にすることをおすすめします。
tsconfig.json の設定
{
“compilerOptions”: {
// 必須の設定
“useUnknownInCatchVariables”: true
}
}
導入直後は、プロジェクト全体で大量の赤波線(型エラー)が出るかもしれません。しかし、それは「これまで隠れていた潜在的なクラッシュポイント」が可視化されただけです。
汎用的なユーティリティを作ろう
毎回 `instanceof Error` を書くのが面倒なら、以下のようなユーティリティ関数を共有しておくのが実務でのベストプラクティスです。
/
- どんなものでもErrorオブジェクトとして正規化するヘルパー
/
export const toError = (e: unknown): Error => {
if (e instanceof Error) return e;
// 文字列ならメッセージとして扱い、それ以外はJSON化して文字列にする
const message = typeof e === “string” ? e : JSON.stringify(e);
return new Error(message);
};
// 現場での使用例
try {
// …
} catch (e) {
const error = toError(e); // 常にError型が返るようになる
console.log(error.message);
}
最後に:なぜこの設定が重要なのか
「動けばいい」コードから「壊れない」コードへ。
`useUnknownInCatchVariables` を有効にすることは、単なるコンパイラ設定の変更ではありません。「エラーが起きたとき、我々エンジニアはどう振る舞うべきか」という設計思想の転換なのです。
フロントエンド開発において、予期せぬエラーで画面が真っ白になる(ホワイトスクリーン)ことは、ユーザー体験の最大の敵です。この設定をオンにして、エラーと正面から向き合う文化をチームに定着させてください。それが、プロのフロントエンドエンジニアとしての第一歩です。

コメント