typescriptデコレータを実装する方法は?


207

TypeScript 1.5デコレータ追加されました

誰かがデコレータを実装する適切な方法を示し、可能な有効なデコレータ署名の引数が何を意味するかを説明する簡単な例を提供できますか?

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

さらに、デコレータを実装する際に留意すべきベストプラクティスの考慮事項はありますか?


私自身に注意してください:-) @Injectableデコレータにを注入したい場合は、参照してください
Anand Rockzz

このプロジェクトにある複数の例を見てみましょう。複数のデコレータがあります-非常にシンプルなものもあれば、理解するのが少し難しいかもしれません:github.com/vlio20/utils-decorators
vlio20

回答:


396

私はデコレーターをいじくり回し、ドキュメントが出る前にこれを利用したい人のために私が見つけたものをドキュメント化することにしました。間違いを見つけた場合は、自由に編集してください。

一般的なポイント

  • デコレータは、オブジェクトがインスタンス化されたときではなく、クラスが宣言されたときに呼び出されます。
  • 同じClass / Property / Method / Parameterに複数のデコレータを定義できます。
  • コンストラクターではデコレーターを使用できません。

有効なデコレータは次のとおりです。

  1. デコレータタイプのいずれかに割り当て可能(ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator)。
  2. デコレートされた値に割り当て可能な値(クラスデコレータおよびメソッドデコレータの場合)を返します。

参照


メソッド/正式なアクセサデコレータ

実装パラメーター:

  • target:クラスのプロトタイプ(Object)。
  • propertyKey:メソッドの名前(string| symbol)。
  • descriptor:A TypedPropertyDescriptor—記述子のキーに慣れていない場合は、このドキュメントでそれについて読むことをお勧めしますObject.defineProperty(3番目のパラメーターです)。

例-引数なし

使用する:

class MyClass {
    @log
    myMethod(arg: string) { 
        return "Message -- " + arg;
    }
}

実装:

function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const originalMethod = descriptor.value; // save a reference to the original method

    // NOTE: Do not use arrow syntax here. Use a function expression in 
    // order to use the correct value of `this` in this method (see notes below)
    descriptor.value = function(...args: any[]) {
        // pre
        console.log("The method args are: " + JSON.stringify(args));
        // run and store result
        const result = originalMethod.apply(this, args);
        // post
        console.log("The return value is: " + result);
        // return the result of the original method (or modify it before returning)
        return result;
    };

    return descriptor;
}

入力:

new MyClass().myMethod("testing");

出力:

メソッドの引数は次のとおりです:["testing"]

戻り値は次のとおりです。メッセージ-テスト

ノート:

  • 記述子の値を設定するときは、矢印構文を使用しないでください。の場合、コンテキストはthisインスタンスのものではありません。
  • 新しい記述子を返すことで現在の記述子を上書きするよりも、元の記述子を変更する方が適切です。これにより、別のデコレータが行ったことを上書きせずに記述子を編集する複数のデコレータを使用できます。これを行う@enumerable(false)@log、次のようなものを同時に使用できます(例:悪い vs 良い
  • 便利:の型引数をTypedPropertyDescriptor使用して、デコレータを配置できるメソッドシグネチャ(メソッドの例)またはアクセサシグネチャ(アクセサの例)を制限できます。

例-引数あり(デコレータファクトリ)

引数を使用する場合は、デコレータのパラメータを使用して関数を宣言し、引数なしで例のシグネチャを使用して関数を返す必要があります。

class MyClass {
    @enumerable(false)
    get prop() {
        return true;
    }
}

function enumerable(isEnumerable: boolean) {
    return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
        descriptor.enumerable = isEnumerable;
        return descriptor;
    };
}

静的メソッドデコレータ

メソッドデコレータに似ていますが、いくつかの違いがあります。

  • そのtargetパラメーターは、コンストラクター関数自体であり、プロトタイプではありません。
  • 記述子は、プロトタイプではなく、コンストラクター関数で定義されます。

クラスデコレータ

@isTestable
class MyClass {}

実装パラメーター:

  • target:デコレータが(TFunction extends Function)で宣言されているクラス。

使用例:メタデータAPIを使用してクラスに情報を格納します。


プロパティデコレータ

class MyClass {
    @serialize
    name: string;
}

実装パラメーター:

  • target:クラスのプロトタイプ(Object)。
  • propertyKey:プロパティの名前(string| symbol)。

使用例@serialize("serializedName")デコレーターを作成し、シリアル化するプロパティのリストにプロパティ名を追加します。


パラメータデコレータ

class MyClass {
    myMethod(@myDecorator myParameter: string) {}
}

実装パラメーター:

  • target:クラスのプロトタイプ(FunctionFunction機能しなくなったようです。クラス内でデコレータを使用するには、anyまたはObjectここでを使用する必要があります。または、制限するクラスタイプを指定してください)
  • propertyKey:メソッドの名前(string| symbol)。
  • parameterIndex:関数のパラメーターのリスト内のパラメーターのインデックス(number)。

簡単な例

詳細な例


パラメータデコレータの例がどこにあるか知っていますか?github.com/Microsoft/TypeScript/issues/…で
Remo H. Jansen

1
@OweRReLoaDeDデコレータに渡されたものをログアウトするだけのパラメータデコレータの下に例を追加しました。それが役立つかどうかはわかりません。今のところ良い例は思いつきません。
David Sherret、2015年

参考までに、githubでこの情報を収集して調整しました:github.com/arolson101/typescript-decorators
arolson101

--experimentalDecoratorsフラグは、この例が機能するために設定する必要があります
Trident D'Gao

私は少し何のと混同していtargetたりprototype of the classkey、誰かがその上で手の込んだてください可能性を意味し、?
Satej S

8

他の回答には見られない重要な点の1つ:

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

デコレータを宣言に適用する方法をカスタマイズする場合は、デコレータファクトリを作成できます。デコレーターファクトリーは、実行時にデコレーターによって呼び出される式を返す関数です。

// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string):  {
    return function(target) {
        // this is the decorator, in this case ClassDecorator.
    }
}

@Entity("cust")
export class MyCustomer { ... }

TypeScriptハンドブックデコレータの章を確認してください。


4
class Foo {
  @consoleLogger 
  Boo(name:string) { return "Hello, " + name }
}
  • ターゲット:上記の場合のクラスのプロトタイプは「Foo」です
  • propertyKey:呼び出されたメソッドの名前。上記の場合は「Boo」
  • ディスクリプター:オブジェクトの説明=> valueプロパティが含まれます。これは関数自体です。function(name){return 'Hello' + name; }

コンソールへの各呼び出しをログに記録するものを実装できます。

function consoleLogger(target: Function, key:string, value:any) 
{
  return value: (...args: any[]) => 
  {
     var a = args.map(a => JSON.stringify(a)).join();
     var result = value.value.apply(this, args);
     var r = JSON.stringify(result);

     console.log('called method' + key + ' with args ' + a + ' returned result ' + r);

     return result;
  }     
}

1
これを厳密なコンパイラ設定でコンパイルするのは
困難

実際、これは間違っていてコンパイルできません。戻り値{value:...}の直後に中括弧が必要です。-これも、あなたのコードの潜在的な供給源から見ることができblog.wolksoftware.com/...
PandaWood
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.