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 で @person 関数を実装する方法

この記事ではvueを使用し、マウスクリックイベントといくつかの小さなページの最適化を追加します。 基...

HTML テーブル マークアップ チュートリアル (28): セルの境界線の色属性 BORDERCOLOR

テーブルを美しくするために、セルごとに異なる境界線の色を設定できます。基本的な構文<TD 境界...

ウェブデザインで注意すべき検索最適化の知識

1. 新サイトホームページのリンクレイアウト1. リンク配置の位置:リンク配置の位置によって、リンク...

HTML テーブルタグチュートリアル (13): 内部境界スタイル属性ルール

RULES を使用すると、テーブルの内部境界のスタイルを制御できます。基本的な構文<TABLE...

MySQL マスタースレーブレプリケーションでエラーをスキップする方法

1. 従来のbinlogマスタースレーブレプリケーション、エラー報告をスキップする方法 mysql&...

MySQL 全文あいまい検索 MATCH AGAINST メソッドの例

MySQL 4.x 以降では、全文検索 MATCH ... AGAINST モード (大文字と小文字...

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

この記事では、MySQL 8.0.13のインストールと設定のチュートリアルを参考までに紹介します。具...

Nginx がフロントエンド リソースへのクロスドメイン アクセスの問題をどのように解決するかの詳細な説明

フロントエンドのクロスドメイン問題に2日間近く悩まされましたが、ようやくngnxを使って解決したので...

Vue3 のウォッチの使用方法とベストプラクティスガイド

目次序文🌟 1. APIの紹介2. 複数のデータソースの監視3. リスニングアレイ4. 監視対象5....

Reactは無限ループスクロール情報を実装する

この記事では、無限ループスクロールを実現するためのReactの具体的なコードを参考までに紹介します。...

完璧なアロエベラジェルを選ぶには?完璧なアロエベラジェルの本物と偽物の見分け方

最新のパーフェクト アロエ ベラ ジェルのパッケージ ボックスには、赤いフォントで完璧な英語の文字が...

AngularJSにおける括弧の役割の詳細な説明

1. 括弧の役割1.1 角括弧 [ ]属性名が角括弧で囲まれている場合、右側には式の値が割り当てられ...

dockerネットワーク双方向接続の詳細な説明

Dockerネットワークを見るdocker ネットワーク ls [root@master ~]# d...

CentOS 7.x dockerはoverlay2ストレージ方式を使用する

/etc/docker/daemon.json を編集し、以下を追加します。 { "ストレ...