TypeScript デコレータ定義

TypeScript デコレータ定義

序文:

Decorator ECMAScriptで提案されていますが、まだ確定していません。TypeScript では実装されていますが、まだ実験的な機能です。デコレータを使用する場合は、 tsconfig.jsonexperimentalDecoratorsプロパティを true に設定する必要があります。

1. コンセプト

1.1 定義

デコレータは、クラス宣言、メソッド、アクセサー、プロパティ、およびパラメータに適用できる新しいタイプの宣言です。デコレータは、 @ 記号と関数名 (例: @testDecorator ) とともに使用されます。ここで、 testDecorator関数であるか、関数を返す必要があります。この関数は実行時に呼び出され、デコレートされたステートメントは自動的にパラメーターとして渡されます。

デコレータは変更するコンテンツの直前に配置する必要があり、すべてのデコレータは宣言ファイルや外部コンテキスト ( declareなど) で使用できないことに注意してください。

デコレータの定義と使用方法は次のとおりです。

// 使用する関数をデコレータ関数として定義します function testDecorator() {}

// @ シンボルを介してデコレータ @testDecorator を使用する

1.2 デコレータファクトリー

いわゆるデコレータファクトリも関数です。通常のデコレータ関数との違いは、戻り値が関数であり、返された関数がデコレータによって呼び出される関数として使用されることです。デコレータファクトリを使用する場合、使用時に現在の使用状況に応じて異なるパラメータを渡すことができますが、使用時に関数呼び出しを追加する必要があります。

サンプルコードは次のとおりです。

// デコレータファクトリ、戻り値は関数です function testDecorator() {
    関数() を返す {}
}

// @symbol + 関数呼び出しでデコレータ @testDecorator() を使用する

1.3 デコレータの組み合わせ

デコレータは組み合わせて使用​​できます。つまり、1 つのターゲットを使用して複数のデコレータを参照できます。

サンプルコードは次のとおりです。

// 2つのデコレータ関数を定義します。function setName() {}
関数 setAge() {}

//デコレータ@setNameを使用する
@set年齢
クラス Person {}

複数のデコレータが使用されている場合、デコレータは順番に実行され、実行順序は次のようになります。

通常のデコレータ関数を使用する場合、実行順序は下から上になります。

サンプルコードは次のとおりです。

関数setName(コンストラクタ: any) {
  console.log('setName', コンストラクター)
}
関数setAge(コンストラクタ: any) {
  console.log('setAge', コンストラクター)
}
@setName
@set年齢
クラス Person {}
/* 実行結果は以下のとおりです。
setAge [関数: 人]
setName [関数: 人]
*/


デコレータ ファクトリの場合、その実行順序は、ファクトリ関数を上から下に実行し、次にファクトリ関数によって返された関数を下から上に実行します。サンプルコードは次のとおりです

関数setName() {
  console.log('getsetName') を実行します。
  関数を返す(コンストラクタ:任意){
    console.log('setName', コンストラクター)
  }
}
関数setAge() {
  console.log('get setAge')
  関数を返す(コンストラクタ:任意){
    console.log('setAge', コンストラクター)
  }
}
@setName()
@set年齢()
クラス Person {}
/* 実行結果は以下のとおりです。
setName を取得する
年齢設定を取得する
setAge [関数: 人]
setName [関数: 人]
*/

1.4 デコレータの評価

クラス定義内の異なる宣言のデコレータは、以下に指定された順序で適用されます。

  • 各インスタンス メンバーには、パラメーター デコレータ、メソッド デコレータ、アクセサ デコレータ、またはプロパティ デコレータが適用されます。
  • 各静的メンバーには、パラメータ デコレータ、メソッド デコレータ、アクセサ デコレータ、またはプロパティ デコレータが適用されます。
  • パラメータ デコレータはコンストラクターに適用されます。
  • クラス デコレータはクラスに適用されます。

2. クラスデコレータ

クラス デコレータはクラス宣言の前に使用され、装飾するコンテンツの直前に配置する必要があります。クラス デコレータはクラス宣言に適用されます。

クラス デコレータ式は実行時に関数として呼び出され、このクラスのコンストラクターである 1 つのパラメーターを持ちます。

サンプルコードは次のとおりです。

記号 = null とする
関数setName() {
  関数を返す (コンストラクタ: Function) {
    記号 = コンストラクタ
  }
}
@setName()
クラス情報{
  コンストラクタ() {}
}
console.log(sign === Info) // true
console.log(sign === Info.prototype.constructor) // true

上記のコードから、クラスInfoのプロトタイプ オブジェクトのconstructorプロパティが実際にはInfo自体を指していることがわかります。

デコレータを使用して、クラスのプロトタイプ オブジェクトとコンストラクターを変更することもできます。サンプル コードは次のとおりです。

// * プロトタイプオブジェクトとコンストラクタ関数を変更する addName(constructor: { new (): any }) {
  constructor.prototype.name = '一碗の周'
}
@名前を追加
クラス Person {}
定数 person = 新しい Person()
console.log(person.name) // エラー プロパティ「name」はタイプ「A」に存在しません

上記のコードでは、 addName修飾子を使用して、 Personクラスのプロトタイプに name 属性を追加し、Person クラスによってインスタンス化されたオブジェクトが name 属性にアクセスできるようにしています。ただし、実際にはそうではありません。ここで例外がスローされています。この問題を解決するには、型アサーションを使用するか、同じ名前のインターフェースを定義してマージによって宣言します。

サンプルコードは次のとおりです。

関数 addName(コンストラクタ: { new (): any }) {
  コンストラクター.prototype.name = '一碗の周'
}
@名前を追加
クラス Person {}
定数 person = 新しい Person()
// 1. 型アサーション // console.log((person as any).name) // 一碗周 // 2. 同じ名前のインターフェースを定義し、マージインターフェースを宣言する Person {
  名前: 文字列
}

console.log(person.name) // 周一万

また、デコレータを介してコンストラクターをオーバーロードすることもできます。サンプル コードは次のとおりです。

// * コンストラクタ関数をオーバーロードします classDecorator<T extends { new (...args: any[]): {} }>(
  コンストラクタ: T,
){
  戻りクラスはコンストラクタを拡張します {
    名前 = '一碗の周'
    趣味 = 「コーディング」
  }
}
@クラスデコレータ
クラス Person {
  年齢 = 18
  名前: 文字列
  コンストラクタ(名前: 文字列) {
    this.name = 名前
  }
}
const person = new Person('Yiwan Zhou')
console.log(人)
/* 実行結果は以下のとおりです。
{
  年齢: 18歳
  名前:「周の一杯」
  趣味:「コーディング」
}
*/

デコレータ ファクトリを通じてパラメータを渡すこともできます。サンプル コードは次のとおりです。

// デコレータファクトリー関数を定義する classDecorator(_name: string) {
  関数 <T extends { new (...args: any[]): {} }>(コンストラクタ: T) { を返します。
    戻りクラスはコンストラクタを拡張します {
      名前 = _名前
      趣味 = 「コーディング」
    }
  }
}
@classDecorator('一碗の周')
クラス Person {
  年齢 = 18
  名前: 文字列
  コンストラクタ(名前: 文字列) {
    this.name = 名前
  }
}
const person = new Person('お粥一杯')
console.log(人)
/* 実行結果は以下のとおりです。
{
  年齢: 18歳
  名前:「周の一杯」
  趣味:「コーディング」
}
*/

3. メソッドデコレータ

メソッド デコレータは、クラス内のメソッドを処理するために使用されます。メソッドのプロパティ記述子 (プロパティ記述子については、Object.defineProperty() を参照してください) とメソッド定義を処理できます。メソッド デコレータも実行時に関数として呼び出され、3 つのパラメータが含まれます。

詳細は以下の通りです。

静的メンバーの場合はクラス コンストラクター、インスタンス メンバーの場合はクラス プロトタイプ オブジェクトです。

メンバーの名。

メンバーのプロパティ記述子。

コード出力が ES5 未満のバージョンを対象としている場合、プロパティ記述子はundefinedなることに注意してください。

次のコードは、デコレータ ファクトリを通じて単純なメソッド デコレータを定義します。サンプル コードは次のとおりです。

// デコレータファクトリー関数 enumerable(bool: boolean) {
  /**
   * メソッド デコレータは 3 つのパラメータを受け入れます。
   * 1. ターゲット: 静的メンバーの場合はクラスのコンストラクタ、インスタンス メンバーの場合はクラスのプロトタイプ オブジェクトです。* 2. propertyName: メンバーの名前。* 3. 記述子: プロパティ記述子。その型は PropertyDescriptor です。
   */
  戻り関数(
    対象: 任意、
    プロパティ名: 文字列、
    記述子: PropertyDescriptor、
  ){
    //渡されたbool記述子に基づいてメソッドが列挙可能かどうかを判断します。enumerable = bool
  }
}
クラス情報{
  コンストラクター(パブリック名: 文字列) {}
  @列挙可能(false)
  取得名() {
    this.name を返す
  }
}
const info = new Info('今週の一杯')
// 直接印刷する場合、メソッドは列挙可能ではないため、オブジェクトには getName() メソッドが含まれません。
console.log(info) // { name: 'Yiwan Zhou' }
// ただし、このメソッドは console.log(info.getName()) で呼び出すことができます // Yiwan Zhou

上記のコードでは、デコレータを通じてクラス内のメソッドのプロパティ記述子を直接変更しました。

メソッド デコレータが値を返す場合、この値はメソッドのプロパティ記述子オブジェクトとして使用されます。サンプル コードは次のとおりです。

// デコレータファクトリー関数 enumerable(bool: boolean) {
  戻り関数(
    対象: 任意、
    プロパティ名: 文字列、
    記述子: PropertyDescriptor、
  ){
    戻る {
      値: 関数 () {
        「エラー: 名前が未定義です」を返します
      },
      列挙可能: ブール値、
    }
  }
}
クラス情報{
  コンストラクター(パブリック名: 文字列) {}
  @列挙可能(false)
  取得名() {
    this.name を返す
  }
}
const info = new Info('A bowl of Zhou')
console.log(info) // { name: 'Yiwan Zhou' }
console.log(info.getName()) // エラー: 名前が未定義です

上記のコードでは、メソッド デコレータは、値プロパティがメソッド定義を変更するオブジェクトを返すため、最終結果はError: name is undefinedなります。

4. アクセサデコレータ

アクセサ デコレータは、前に学習したsetメソッドとgetメソッドであり、1 つはプロパティ値を設定するときにトリガーされ、もう 1 つはプロパティ値を取得するときにトリガーされます。

アクセサ デコレータもメソッド デコレータと同様に 3 つのパラメータを受け入れるため、ここでは詳細には説明しません。

サンプルコードは次のとおりです。

関数列挙可能(bool: boolean) {
  戻り関数(
    対象: 任意、
    プロパティ名: 文字列、
    記述子: PropertyDescriptor、
  ){
    記述子.enumerable = bool
  }
}
クラス情報{
  private_name: 文字列
  コンストラクタ(名前: 文字列) {
    this._name = 名前
  }
  @列挙可能(false)
  名前を取得する() {
    this._name を返す
  }
  名前を設定する(名前) {
    this._name = 名前
  }
}

TypeScriptでは、メンバーのgetアクセサーとsetアクセサーの両方を装飾することはできないことに注意してください。

5. プロパティデコレーター

プロパティ デコレータはプロパティ宣言の前に宣言され、次に示すように 2 つのパラメータを持ちます。

  • 静的メンバーの場合はクラス コンストラクター、インスタンス メンバーの場合はクラス プロトタイプ オブジェクトです。
  • メンバーの名。

サンプルコードは次のとおりです。

関数 printPropertyName(ターゲット: 任意、プロパティ名: 文字列) {
  console.log(プロパティ名)
}
クラス情報{
  @printプロパティ名
  名前: 文字列
  @printプロパティ名
  年齢: 番号
  コンストラクター(名前: 文字列、年齢: 数値) {
    this.name = 名前
    this.age = 年齢
  }
}
新しい情報('Yiwan Zhou'、18)

実行結果は次のとおりです。

名前

6. パラメータデコレータ

パラメータ デコレータには次の 3 つのパラメータがあります。

  • 静的メンバーの場合はクラス コンストラクター、インスタンス メンバーの場合はクラス プロトタイプ オブジェクトです。
  • メンバーの名。
  • 関数のパラメータ リスト内のパラメータのインデックス。

パラメータ デコレータの機能は、メソッド パラメータが渡されたかどうかを監視することです。パラメータ デコレータの戻り値は無視されます。

サンプルコードは次のとおりです。

関数が必要です(ターゲット: 任意、プロパティ名: 文字列、インデックス: 数値) {
  console.log(`変更されたパラメータは ${propertyName} の ${index + 1} 番目のパラメータです`)
}
クラス情報{
  name: string = '一碗の周'
  年齢: 番号 = 18
  getInfo(プレフィックス: 文字列、@必須情報タイプ: 文字列): 任意 {
    プレフィックス + ' ' + this[infoType] を返す
  }
}
インターフェース情報{
  [キー: 文字列]: 文字列 | 数値 | 関数
}
const info = 新しい Info()
info.getInfo('', 'age') // getInfoの2番目のパラメータを変更します

ここではgetInfoメソッドの 2 番目のパラメータの前にパラメータ デコレータを使用して、デコレータ内で情報を取得できるようにしています。

TypeScript デコレータ定義に関する記事はこれで終わりです。TypeScript デコレータについてさらに詳しく知りたい方は、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続きご覧ください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • TypeScript で Error クラスを継承する方法を簡単に説明します
  • JavaScript のプライベート クラス フィールドと TypeScript のプライベート修飾子の詳細な説明
  • TypeScript インターフェース定義ケースチュートリアル
  • TypeScript 関数の定義と使用例のチュートリアル
  • TypeScript 学習ノート: TypeScript クラス定義、クラス継承、クラス メンバー修飾子

<<:  親コンテナの CSS 均等分割 (完全三分の一) の実装

>>:  複数の古いプレーヤーの埋め込みコード

推薦する

Vue における ref と $refs の紹介と使用例

序文JavaScript では、document.querySelector("#demo...

MySQL 8.0.13 のインストールと設定のグラフィックチュートリアル

Msyqlデータベースのインストール、参考までに具体的な内容は次のとおりです。 ①ブラウザでhttp...

Ubuntu LinuxにOracle Java 14をインストールする方法

最近、Oracle は Java 14 (または Oracle JDK 14) の一般公開を発表しま...

JavaScript タイピングゲーム

この記事では、タイピングゲームを実装するためのJavaScriptの具体的なコードを参考までに紹介し...

Python の MySQL データベース LIKE 演算子の詳細な説明

LIKE 演算子は、列内の指定されたパターンを検索するため、WHERE 句で使用されます。文法: 列...

Ubuntu Server のターミナルのウェルカム メッセージで広告を無効にする方法

最新の Ubuntu Server バージョンを使用している場合、ようこそメッセージに、Ubuntu...

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

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

CSS で text-align と margin: 0 auto を使用して中央に配置する例コード

CSSでtext-align、margin: 0 autoを使用して中央揃えにするtext-alig...

SQLのさまざまな結合サマリーの詳細な説明

SQL 左結合、右結合、内部結合、自然結合 さまざまな結合の概要SQL には、左結合、右結合、内部結...

Mysql ルートユーザーアカウントのパスワードをリセットする問題を解決する

問題の説明: mysqladmin.exe を使用してコマンドを実行すると、次のエラー メッセージが...

MySQL で二重引用符の位置が誤っていたために起きた殺人事件の詳細な分析

1. はじめに最近、開発者が誤ってデータを削除したり更新したりするケースがよくあります。今回もまた問...

Sublime TextがUbuntuで中国語を入力できない問題の最も簡単な解決策

崇高なSublime Text はコード エディター (Sublime Text2 は有料ソフトウェ...

Vueは小さな天気予報アプリケーションを実装します

これは私が Vue フレームワークを独学していたときに真似したウェブサイトです。いくつかの都市の天気...

vsFTP 3.0.3 のコンパイルとインストールの詳細な分析

脆弱性の詳細VSFTP は、GPL に基づいてリリースされた Unix ライクなシステムで使用される...

制限およびオフセット ページング シナリオを使用すると速度が遅くなるのはなぜですか?

質問から始めましょう5 年前、私が Tencent にいたとき、ページング シナリオでは MySQL...