TypeScriptにおける型の絞り込み(Type Narrowing)は、コンパイラが特定のコードの領域において、変数が条件に基づいて明確により具体的な型を持つことを「理解する」プロセスです。
一般的な絞り込みのテクニックには、以下のものがあります:
typeof演算子によるチェック:function example(x: number | string) { if (typeof x === 'string') { // x: string ここで return x.toUpperCase(); } else { // x: number ここで return x.toFixed(2); } }
instanceofによるチェック(クラス用):if (dateObj instanceof Date) { // dateObj: Date }
nullおよびundefinedのチェック:function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }
TypeScriptはカスタム型ガード関数もサポートしています:
function isString(x: unknown): x is string { return typeof x === 'string'; }
型の絞り込みは型チェックを安全にし、コードをより信頼性の高いものにします。
通常の比較(例えば
==/===)によって型の絞り込みを保証できますか?それは常に機能しますか?
回答: いいえ。TypeScriptは単純な比較から型を理解するわけではなく、特に比較があまりにも「曖昧」な場合や、間接的な変数/プロパティを介して行われる場合には、型の絞り込みが行えません。絞り込みには明示的なメカニズム(typeof、instanceof、識別子プロパティおよび型ガード)を使用する必要があります。
例:
function foo(x: number | string | null) { if (x) { // x: string | number、nullの可能性はないが、具体的な型の絞り込みは行われない } }
逸話
大規模なTypeScriptプロジェクトで、条件型user.role == 'admin'では型が絞り込まれず、プロパティの存在チェックを記述する必要がありました。開発者たちは絞り込みルールを過小評価し、「Cannot read property ... of undefined」というエラーを引き起こしました。
逸話
モバイルアプリケーションで、関数はオブジェクトまたは文字列のいずれかを受け取っていました。型を変更する関数を間接的に呼び出すことで絞り込みが行われず、いくつかのデバイスで文字列に存在しないメソッドを呼び出すとクラッシュしました。稀なケースでのテストが失敗しました。
逸話
JavaScriptからTypeScriptへのコードの移行で、プロパティの存在チェックが常に型を絞り込むだろうと予想して、カスタム型ガード関数を実装しませんでした。その結果、オプションフィールドを持つ複雑なオブジェクトが期待通りに動作せず、ランタイムでのデータアクセスの予測不可能なエラーが発生しました。