Programmingフロントエンド/バックエンド開発者

TypeScriptにおけるStrict Function Typesオプションはどのように機能し、動作するのか?それは、共変性と反変性を持つ関数の型検査にどのように影響を与え、シグネチャの不一致がコンパイルエラーにつながるのはどのような場合か?

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

回答。

質問の背景

デフォルトでは、TypeScriptは関数の型シグネチャを照合する際に一定の「ゆるさ」を許容しており、逆変換関数と非逆変換関数が互換性があると見なされています。TypeScript 2.6から導入されたstrictFunctionTypesオプションは、関数の型の厳密な検査を保証し、特に大規模なコードベースでは多くのエラークラスを防止します。

問題点

厳密な検査がない場合、ハンドラー関数やコールバックが、より多くまたはより特定的なタイプのパラメータを受け取るという状況が発生し、これが開発者にとって見えないことがあります。これは、返される型の共変性や引数の反変性に関連するランタイムエラーを引き起こします。

解決策

strictFunctionTypesオプションは、関数のパラメータ型に対して厳密な反変性を導入します。これにより、関数は、元の型のパラメータが宛先のパラメータのスーパタイプである場合にのみ互換性があります。

コードの例:

type Animal = { name: string }; type Cat = { name: string; meow: () => void }; let animalHandler: (a: Animal) => void; let catHandler: (c: Cat) => void; animalHandler = catHandler; // strictFunctionTypesエラー: 引数が特定的すぎる catHandler = animalHandler; // 許可される、CatはAnimalのサブタイプ

主な特徴:

  • 関数の引数は「スーパタイプ」の互換性を検査される(反変性)
  • 返される値は共変性(サブタイプが許可される)に基づいて検査される
  • シグネチャの違反がコンパイルエラーを引き起こす

トリッキーな質問。

厳密なチェックが導入される前に、より特定的なパラメータタイプのハンドラーを割り当てることはできたか?

はい、strictFunctionTypesが有効になる前は、TypeScriptは、より特定的な関数を一般的な関数に割り当てることを許可していました。これは、ランタイムエラーにつながることがありました。

enum E { A, B } const f: (e: E) => void = (e: E.A) => {} // strictFunctionTypesなし: 許可される

strictFunctionTypesはオプションパラメータのコールバックにどのように影響するか?

関数のコールバックのパラメータがオプションパラメータになる場合、厳格なチェックは、必須パラメータの数が多い関数の位置に、必須パラメータが少ない関数を使用することを許可しません。これは、コールバックが必要なデータを受け取れない状況を防ぎます。

古いプロジェクトでstrictFunctionTypesを有効にすると互換性の問題が発生するか?

はい、コンパイルエラーが新たに発生するリスクがあるため、多くの関数やハンドラーが反変性の違反を伴ってお互いに割り当てられている可能性があります。これは、コールバックや、厳密な型指定なしでのサードパーティライブラリのAPIの使用時に最も一般的に見られます。

タイプエラーとアンチパターン

  • パラメータの厳密なチェックなしで古い型指定のコールバックを使用する
  • より特定的/狭いパラメータタイプの関数を一般的なハンドラーに割り当てようとする
  • strictFunctionTypesを有効にしたときのエラーを無視する(型を修正する代わりにオプションをコメントアウトする)

実生活の例

ネガティブケース

大規模なプロジェクトで、イベントハンドラーがより特定的な型(一般的なEventの代わりにMouseEvent)を受け取ります。これは厳密なオプションが有効にされるまで検出されず、異なるイベント源で実行時エラーを引き起こします。

利点:

  • より迅速なプロトタイピング

欠点:

  • イベントタイプの不一致によるランタイムバグ
  • コード拡張後の難しいデバッグ

ポジティブケース

プロジェクトは最初からstrictFunctionTypesを使用しています。新しいハンドラーを追加すると、型間のすべての不一致がコンパイラによって自動的に検出されます。コードはタイポに対してより耐性があり、メンテナンスが容易になります。

利点:

  • 信頼性
  • 関数とハンドラーの安全な伝達
  • リファクタリング時の予測可能な動作

欠点:

  • シグネチャの詳細な設計が必要
  • 一部のケースでは、互換性のために追加のラッパーやオーバーロードを書く必要があること