タイプガードとは、特定のチェック(例えば、typeof、instanceof、またはparam is SomeTypeの形の式を返す特別な関数など)に基づいて、コードブロック内の変数のタイプを明確化する仕組みです。
主な利点 — コンパイル時のタイプチェックによる安全性と実行時エラーの排除です。
例:
interface Fish { swim: () => void } interface Bird { fly: () => void } function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined; } function move(pet: Fish | Bird) { if (isFish(pet)) { pet.swim(); } else { pet.fly(); } }
ここでの関数isFishはカスタムタイプガードです。
注意点:
質問: "TypeScriptコンパイラは常にガード関数の戻り値のみを信頼するのか、それとも関数内で他の分析も行うのか?"
答え:
TypeScriptコンパイラは、戻り値のシグネチャparam is Typeのみに依存しています。ガード関数内で何が起こるかは、実装の正確性について分析されません。
例(危険なエラー!):
function isString(x: any): x is string { return true; } // コンパイラは常に文字列であると認識しますが、実際にはそうではありません: if (isString(123)) { // ここではxはstring型ですが、実際には数値です }
物語
フロントエンドとバックエンド間で共有されるDTOのプロジェクトでは、カスタムタイプガード内で厳密なチェックを追加するのを忘れていました。その結果、一部のデータが誤って必要な型として認識され、存在しないプロパティを使用しようとした際にクライアントで障害が発生しました。
物語
開発者はオプションのフィールドに依存してタイプガードを記述しましたが、データ構造はそのフィールドを持たないことを許可していました。その結果、型ごとのswitch-caseで分岐が不足し、コンパイラは警告を出さず、ランタイムで例外が発生しました。
物語
あるサービスでは、TypeScriptへの移行時に組み込みのタイプガード(typeof、instanceof)のみに依存しました。プロトタイプが実行中に変更されると、チェックが不正確になり、デバッグが難しいバグが生じました。