Programmingフロントエンド開発者

TypeScriptはユーザー定義タイプガード(ユーザー定義の型ガード関数)とどのように機能し、何のために必要で、正しいタイププレディケート関数を作成するにはどうすればよいですか?実装時によくある罠は何ですか?

Hintsage AIアシスタントで面接を突破

回答。

質問の背景:TypeScriptは通常の型チェックを拡張し、開発者がオブジェクトが特定の型に従っているかどうかを確認するためのユーザー定義の関数、すなわちタイプガードを作成できるようにします。これは、型値が異なる可能性があるユニオン型、動的構造、およびAPIで作業するために必要です。

通常のtypeofやinstanceofによる型チェックはプリミティブとクラスに制限されているため、構造や複雑な型はこれだけでは特定できません。コンパイラに対してどのケースで値が必要な型に安全に絞られるかを明示的に示す必要があります。

解決策として、関数はタイププレディケートを持つ形で書かれます。例えば、function isCat(obj: Animal): obj is Cat {...}の形式です。ここでの重要なポイントは、関数の返り値の型におけるタイププレディケートです。

コード例:

interface Dog { bark: () => void; } interface Cat { meow: () => void; } type Pet = Dog | Cat; function isDog(pet: Pet): pet is Dog { return (pet as Dog).bark !== undefined; } function makeSound(pet: Pet) { if (isDog(pet)) { pet.bark(); } else { pet.meow(); } }

主な特徴:

  • 型チェックはparam is Typeの形式の特別なタイププレディケート関数を通じて実装されます。
  • 自分のガードを作成し、オブジェクトクラスだけでなく、任意の構造に対して使用できます。
  • 不正確なタイプガードは実行時エラーを引き起こし、型の絞り込みを誤る可能性があります。

トリッキーな質問。

タイプガード関数でtrue/falseを返すだけでコンパイラが型を絞り込むのに十分ですか?

いいえ。返り値の型をtype predicate(例えば、pet is Dog)の形式で明示的に指定することが重要です。そうでないと、TypeScriptは自動的に値の型を絞り込みません。関数がtrueまたはfalseのみを返してもです。

コールバック内でタイプガード(例えば、filter内)を使用できますか?その場合、絞り込みは正しく機能しますか?

はい。タイプガードが正しくアノテートされていれば、コンパイラはfilterの後で配列の要素の型を絞り込むことができます。forEach/callback関数内でも同様です。ただし、アノテーションが欠如または誤っている場合、結果は特定の型ではなくユニオンの型になります。

const pets: Pet[] = [...]; const dogs = pets.filter(isDog); // TypeScriptはdogsの型がDog[]であることを知っています

ユーザー定義タイプガードは、typeofやinstanceofを用いた通常の型チェックとは異なりますか?

タイプガード関数は任意の構造のチェックを実装し、複雑さのレベルを問わずチェックを記述し、クラスのないインターフェースを操作できるため、基本の型やクラスだけでなく、より柔軟です。

型エラーとアンチパターン

  • タイプガードがtype-predicateタイプを返さず、単にbooleanを返す — コンパイラに対する効果がない。
  • 表面的なキーしかチェックせず、構造の完全性を保証しない。
  • タイプガードが常にtrueを返し、実質的に型の保護を無効にする。
  • 入力パラメータがあまりにも一般的であるか、検証なしで暗黙の型変換が行われる。

実例

ネガティブケース

関数がユーザーをフィルタリングし、タイププレディケートなしでタイプガードを作成します:

function isValidAdmin(user: any): boolean { return user.isAdmin === true; } const admins = users.filter(isValidAdmin); // admins: any[]

メリット:

  • 速い、効果はランタイムで明確

デメリット:

  • フィルタリング後のデータタイプはTypeScriptに対して уточняされず、プロパティへのアクセス時にエラーが発生する可能性があります。

ポジティブケース

正しいタイププレディケートでフィルタリングする関数:

interface Admin { name: string; isAdmin: true; } function isAdmin(user: any): user is Admin { return user && user.isAdmin === true; } const admins = users.filter(isAdmin); // admins: Admin[]

メリット:

  • 結果の型が正確に把握できる
  • 結果での作業時のエラーの可能性が減少する

デメリット:

  • シグニチャとチェックのカバレッジに対する注意が若干必要です。