TypeScript での関数オーバーロード

TypeScript での関数オーバーロード

序文:

ほとんどの関数は固定されたパラメータセットを受け入れます。ただし、一部の関数では、関数の呼び出し方法に応じて、可変数の引数、異なる型の引数を受け入れたり、異なる型を返したりすることもできます。このような関数に注釈を付けるために、TypeScript は関数オーバーロードを提供します。

1. 関数シグネチャ

まず、特定の人に挨拶メッセージを返す関数を考えてみましょう。

関数greet(人: 文字列): 文字列 {
  `Hello, ${person}!` を返します。
}


上記の関数は、文字型の引数 1 つ (人の名前) を受け入れます。関数の呼び出しは非常に簡単です:

greeting('World'); // 'こんにちは、World!'


greet()関数をより柔軟にしたい場合はどうすればよいでしょうか?たとえば、挨拶する人のリストを追加で受け入れるようにします。

このような関数は、文字列または文字列の配列を引数として受け入れ、文字列または文字列の配列を返します。

このような関数に注釈を付けるにはどうすればよいでしょうか?方法は2つあります。

最初のアプローチは簡単で、パラメータと戻り値の型を更新して関数シグネチャを直接変更します。

リファクタリング後のgreet()は次のようになります。

関数greet(person: 文字列 | 文字列[]): 文字列 | 文字列[] {
  if (typeof person === 'string') {
    `Hello, ${person}!` を返します。
  } そうでない場合 (Array.isArray(person)) {
    person.map(name => `こんにちは、${name}!`) を返します。
  }
  新しいエラーをスローします('挨拶できません');
}


これで、greet() を 2 つの方法で呼び出すことができます。

greeting('World'); // 'こんにちは、World!'
greeting(['Xiaozhi', 'Daye']); // ['こんにちは、Xiaozhi!', 'こんにちは、Daye!']


複数の呼び出しメソッドをサポートするために関数シグネチャを直接更新することは、一般的で適切なアプローチです。

ただし、場合によっては、別のアプローチを採用し、関数を呼び出すすべての方法を個別に定義する必要があります。このアプローチは関数オーバーロードと呼ばれます。

2. 関数のオーバーロード

2 番目の方法は、関数オーバーロードを使用することです。関数シグネチャが比較的複雑で、複数の型が関係する場合は、このアプローチをお勧めします。

関数オーバーロードを定義するには、オーバーロード シグネチャと実装シグネチャを定義する必要があります。

オーバーロードされたシグネチャは、関数本体なしで、関数のパラメーターと戻り値の型を定義します。関数には、関数を呼び出すさまざまな方法に対応する複数のオーバーロードされたシグネチャを含めることができます。

一方、実装シグネチャには、実装関数の本体だけでなく、パラメータの型と戻り値の型もあり、実装シグネチャは 1 つしか存在できません。

// オーバーロードシグネチャ function greeting(person: string): string;
関数greet(persons: string[]): string[];
 
// シグネチャ関数を実装するgreet(person: unknown): unknown {
  if (typeof person === 'string') {
    `Hello, ${person}!` を返します。
  } そうでない場合 (Array.isArray(person)) {
    person.map(name => `こんにちは、${name}!`) を返します。
  }
  新しいエラーをスローします('挨拶できません');
}


greet()関数には、2 つのオーバーロード シグネチャと 1 つの実装シグネチャがあります。

各オーバーロード シグネチャは、関数を呼び出す 1 つの方法を記述します。 greet()関数に関しては、文字列引数を使用するか、文字列の配列を使用するかの 2 つの方法で呼び出すことができます。

実装シグネチャfunction greet(person: unknown): unknown { ... }は、この関数がどのように動作するかに関する適切なロジックが含まれています。

これで、上記のように、 greet()文字列または文字列の配列で呼び出すことができます。

greeting('World'); // 'こんにちは、World!'
greeting(['Xiaozhi', 'Daye']); // ['こんにちは、Xiaozhi!', 'こんにちは、Daye!']


2.1 オーバーロードされたシグネチャは呼び出し可能

実装シグネチャは関数の動作を実装しますが、直接呼び出すことはできません。オーバーロードされたシグネチャのみが呼び出し可能です。

greeting('World'); // オーバーロードされたシグネチャ呼び出し可能関数 greeting(['Xiaozhi', 'Daye']); // オーバーロードされたシグネチャ呼び出し可能関数 const someValue: unknown = 'Unknown';
greeting(someValue); // 実装シグネチャは呼び出し可能ではない

// エラーを報告します。この呼び出しに一致するオーバーロードはありません。
  オーバーロード 1/2、「(person: string): string」で次のエラーが発生しました。
    'unknown' 型の引数は 'string' 型のパラメータに割り当てることができません。
  オーバーロード 2/2、「(persons: string[]): string[]」で次のエラーが発生しました。
    'unknown' 型の引数は 'string[]' 型のパラメータに割り当てることができません。

上記の例では、実装シグネチャがunknown引数を受け入れる場合でもunknown (greet(someValue))を使用してgreet()関数を呼び出すことはできません。

2.1 実装署名は普遍的でなければならない

// オーバーロードシグネチャ function greeting(person: string): string;
関数greet(persons: string[]): string[]; 
// このオーバーロード シグネチャは、実装シグネチャと互換性がありません。

 
// シグネチャ関数を実装するgreet(person: unknown): string {
  // ...
  新しいエラーをスローします('挨拶できません');
}

オーバーロードされたシグネチャ関数greet(person: string[]): string[] greet(person: unknown): stringと互換性がないとしてマークされています。

実装シグネチャのstring戻り型は、オーバーロードされたシグネチャのstring[]戻り型と互換性があるほど汎用的ではありません。

3. メソッドのオーバーロード

前の例では、関数オーバーロードが通常の関数に適用されました。しかし、メソッドをオーバーロードすることもできます

メソッド オーバーロード セクションでは、オーバーロード シグネチャと実装シグネチャの両方がクラスの一部になります。

たとえば、オーバーロードされたメソッドgreet()を持つGreeterクラスを実装します。

クラス Greeter {
  メッセージ: 文字列;
 
  コンストラクター(メッセージ: 文字列) {
    this.message = メッセージ;
  }
 
  // オーバーロードシグネチャ greeting(person: string): string;
  挨拶(人物: 文字列[]): 文字列[];
 
  // シグネチャを実装するgreet(person: unknown): unknown {
    if (typeof person === 'string') {
      `${this.message}, ${person}!` を返します。
    } そうでない場合 (Array.isArray(person)) {
      person.map(name => `${this.message}, ${name}!`) を返します。
    }
    新しいエラーをスローします('挨拶できません');
  }

Greeterクラスには、greet()オーバーロードメソッドが含まれています。メソッドの呼び出し方法を記述する2つのオーバーロードシグネチャと、正しい実装を含む実装シグネチャです。

メソッドのオーバーロードのおかげで、hi.greet() を、文字列または文字列の配列を引数として 2 つの方法で呼び出すことができます

const hi = new Greeter('Hi');
 
hi.greet('Xiaozhi'); // 'こんにちは、Xiaozhi!'
hi.greet(['Wang Daye', 'Daye']); // ['こんにちは、Wang Daye!', 'こんにちは、Daye!']


4. 関数オーバーロードを使用する場合

関数オーバーロードを適切に使用すると、複数の方法で呼び出される可能性のある関数の使いやすさが大幅に向上します。これは、自動補完を行うときに特に便利です。自動補完では、可能なすべてのオーバーロードがリストされます。

ただし、場合によっては、関数オーバーロードではなく、関数シグネチャを使用することをお勧めします。

たとえば、オプションのパラメータを持つ関数のオーバーロードは使用しないでください。

// 推奨されません function myFunc(): string;
関数 myFunc(param1: 文字列): 文字列;
関数 myFunc(param1: 文字列、param2: 文字列): 文字列;
関数 myFunc(...args: 文字列[]): 文字列 {
  // 実装...
}


関数シグネチャでオプションのパラメータを使用するだけで十分です。

// 推奨される方法 function myFunc(param1?: string, param2: string): string {
  // 実装...
}

5. まとめ

TypeScriptの関数オーバーロードを使用すると、複数の方法で呼び出すことができる関数を定義できます。

関数オーバーロードを使用するには、オーバーロード シグネチャ (パラメーターと戻り値の型を持ち、本体を持たない関数のセット) を定義する必要があります。これらのシグネチャは、関数をどのように呼び出すかを示します。

さらに、関数の正しい実装 (実装シグネチャ)、つまりパラメータと戻り値の型、および関数本体** を記述する必要があります。実装シグネチャは呼び出し可能ではないことに注意してください。 **

通常の関数に加えて、クラス内のメソッドもオーバーロードできます。

TypeScriptの関数オーバーロードに関するこの記事はこれで終わりです。TypeScript の関数オーバーロードの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • TypeScript における型とインターフェースの違いの詳細な説明
  • Typescript で for...in を使用する方法
  • TypeScript および JavaScript プロジェクトに MD5 チェックサムを導入する
  • TypeScript コア基盤インターフェース
  • TypeScriptは関数のオーバーロードを宣言するためにタプルユニオンを使用する

<<:  Dockerのプロセスとイメージを実行するための基本コマンドの詳細な説明

>>:  jQuery エディタ プラグイン tinyMCE の使い方

推薦する

HTMLハイパーリンクaタグのhrefジャンプとonclick間の実行順序の例

HTMLハイパーリンクaタグのhrefジャンプとonclickの実行関係htmlのaタグのhrefは...

背景位置パーセンテージ原則の詳細な説明

今日、誰かがコードを調整するのを手伝っていたとき、次のようなスタイルを見つけました。 背景位置: 5...

Linux で ss コマンドと zabbix を組み合わせてソケットを監視する方法の詳細な説明

目次序文1. ssコマンド2. Zabbix監視マシンの全体的なソケットステータス2.1. スクリプ...

Linux で複数のファイルの名前を一度に変更する方法

序文日常業務では、すべての jpg ファイルを bnp に変更したり、名前の 1 を one に変更...

JavaScript で二分探索木を実装する

JavaScriptでの検索二分木実装は参考までに。具体的な内容は以下のとおりです。バイナリ検索木 ...

Linux で Bash 環境変数を設定する方法

Shell は C 言語で書かれたプログラムであり、ユーザーが Linux を使用するための橋渡しと...

新しい要素を作成する3つの方法のまとめ

1つ目: テキスト/HTML経由var txt1="<h1>テキスト。<...

Vue の this.$store.state.xx.xx に関する簡単な説明

目次これを Vue.$store.state.xx.xxストアからデータを取得する私のプロジェクトフ...

JavaScript を使用してテーブル情報を追加および削除する

JavaScript 入門JavaScript は軽量なインタープリタ型の Web 開発言語です。言...

MySQL の詳細な単一テーブルの追加、削除、変更、クエリの CRUD ステートメント

MySQL の追加、削除、変更、クエリステートメント1. 練習シートを作成するここでの練習表は3つの...

MySQL のデッドロックとデータベースおよびテーブル シャーディングの問題の詳細な説明

MySQL 運用上の問題点を記録します。ビジネスシナリオと問題の説明外部インターフェースをリクエスト...

登録ページを実装するためのJS、CSS、HTML

HTML と CSS で実装された登録ページ テンプレート。早速、コードを見てみましょう。更新: ...

Apache Flink の任意の Jar パッケージのアップロードにより、リモート コード実行の脆弱性が再発する問題が発生する (脆弱性警告)

脆弱性の説明Apache Flink は、分散ストリームおよびバッチ データ処理用のオープン ソース...

Linux で libudev を使用して USB デバイスの VID と PID を取得する方法

この記事では、libudev ライブラリを使用して hidraw デバイスにアクセスします。 lib...

dl、dt、dd はいつ使用するのが適切ですか?

dl:定義一覧定義リストdt:定義タイトルタイトルを定義するdd:定義説明定義の説明dt は情報のタ...