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 均等分割 (完全三分の一) の実装

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

推薦する

nginx ウェブサイト サービスのアンチホットリンクを設定する方法 (推奨)

1. ホットリンクの原則1.1 Webページの準備Web ソース ホスト (192.168.153...

HTML ページ出力で従うべきいくつかの原則の要約

1. DOCTYPE は必須です。ブラウザは宣言した DOCTYPE に基づいてページのレンダリング...

回転灯効果を実現するWeChatアプレットの例

序文日常の開発では、テキストの水平スクロール効果(一般にカルーセルと呼ばれる)によく遭遇します。これ...

HTML H タイトルタグの使用

H タグ、特に h1 タグの使用は常に議論の的となっている問題であり、私たちが研究する価値のある問題...

mysql トリガーの作成と使用例

目次トリガーとは何かトリガーを作成するMySQL 作成構文のキーワードの説明: 1. MySQL ト...

Vue フロントエンド開発における階層的にネストされたコンポーネント間の通信の詳細な説明

目次序文例まとめ序文Vue の親子コンポーネントは、props を通じて親コンポーネントの値を子コン...

Vue は QR コード スキャン機能を実装します (スタイル付き)

必要: vue を使用して QR コードのスキャンを実現します。プラグイン: QRコードリーダー;プ...

Vue3 シングルファイルコンポーネントのスタイル機能の詳細な説明

目次スタイルスコープスタイルモジュール状態駆動型動的CSS要約するスタイルスコープ注意事項:スタイル...

Linux システムのユーザー管理コマンドの概要

ユーザーとグループの管理1. ユーザーとグループの基本概念ユーザーとグループ:システム上のすべてのプ...

あまり多くのコードを書かずに、ハイパーリンクを使ってシンプルで美しいカスタムチェックボックスを実装できます。

今日ふと、HTML でチェックボックスのスタイルを変更できる範囲が限られていることと、チェックボック...

HTML4とHTML5の違い: 入力にフォーカス実装コードを追加する方法

html4:コードをコピーコードは次のとおりです。 <フォーム> <p>&l...

Dockerイメージの作成Dockerfileとコミット操作

イメージを構築するイメージを構築するには、主に 2 つの方法があります。実行中のコンテナをイメージに...

DIVマスクを使用して、マウスでチェックボックスを直接チェックすることが無効である問題を解決します

フロントエンドの開発過程で、チェックボックスが必要な状況が発生しました。ユーザー操作の利便性を考慮し...

開発をスピードアップできる VueUse ライブラリ 5 つ (まとめ)

目次VueUse にはどのようなユーティリティがありますか? VueUseをVueプロジェクトにイン...

JavaScript タイマー原理の詳細な説明

目次1. setTimeout() タイマー2. setTimeout() タイマーを停止する3. ...