ProgrammingTypeScript開発者

TypeScriptにおける関数のオーバーロードの型付けはどのように機能し、他の言語のメソッドのオーバーロードとどのように異なるのか?

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

答え。

質問の歴史

JavaやC#のような言語では、関数やメソッドのオーバーロードは標準的な機能であり、同じ名前の関数を異なる引数で複数宣言できます。しかし、JavaScriptにはそれがなく、潜在的なエラーを引き起こす可能性があります。TypeScriptはこの問題を型レベルで解決します。

問題

TypeScriptには実際のオーバーロード(複数の実装)は存在しません。複数の関数シグネチャを宣言できますが、実際の関数は1つだけです。宣言されたシグネチャと実装の間に不整合がある場合や、シグネチャの位置付けが間違っている場合にエラーが発生します。

解決策

TypeScriptは、複数のシグネチャを宣言し、その全てのオーバーロードバリエーションと互換性のある単一の実際の実装を通じて、関数のオーバーロードをサポートします。

コードの例:

function sum(a: number, b: number): number; function sum(a: string, b: string): string; function sum(a: any, b: any): any { return a + b; } const a = sum(1, 2); // 3 const b = sum('foo', 'bar'); // "foobar"

主な特徴:

  • すべてのシグネチャの後に常に1つの実装がある
  • 実装はすべてのオーバーロードバリエーションと互換性がある必要がある
  • TypeScriptはオーバーロードされた関数を使用する際に呼び出しの正しさをチェックします

トリックのある質問。

すべてのオーバーロードのシグネチャを宣言する前に関数の実装を定義できますか?

いいえ、最初にすべてのオーバーロードのシグネチャを宣言し、その後に実装を記述する必要があります。エラー:

// エラー!最初にシグネチャ、その後に実装 function foo(a: number): number { return a } // エラー function foo(a: string): string { return a } // エラー

TypeScriptはすべてのオーバーロードのシグネチャで返り値の型をチェックしますか?

TypeScriptはコンパイル時に宣言されたシグネチャに従って返り値の型を保証しますが、実行時には型のチェックは行われません。

実装内でのすべての入力データのバリエーションをカバーする必要がありますか?

実装する関数は、オーバーロードのシグネチャに従ってすべての入力データのバリエーションを適切に処理する必要があります:

function parse(a: number): string; function parse(a: string): string; function parse(a: number | string): string { return String(a); }

一般的なエラーとアンチパターン

  • 実装とオーバーロードのシグネチャの不一致
  • シグネチャおよび実装の宣言の順序が間違っている
  • 型の組み合わせの暗黙の処理/無視

実生活の例

ネガティブケース

異なる入力型の複数の関数のオーバーロードが宣言されていますが、実装がすべてのケースを処理せず、まれな入力に対してランタイムでエラーをスローします。

利点:

  • 関数のユーザーにとって柔軟なAPI

欠点:

  • 型はコンパイルされますが、関数は内部で追加のチェックなしには正しく機能しません。

ポジティブケース

関数の各オーバーロードが実装内での型に対する条件でカバーされており、型チェック(typeof/instanceof)が使用されています。

利点:

  • 安全で予測可能なAPI
  • エラーがコンパイル時にキャッチされます

欠点:

  • 追加のチェックによりコードベースが増加します