web-dev-qa-db-ja.com

TypeScriptプロパティデコレータはクラスのメタデータを設定できますか?

TypeScriptでは、プロパティデコレータを使用してクラスのメタデータを設定することはできますか?以下のコードを検討してください。クラスデコレータの「ターゲット」は、プロパティデコレータの「ターゲット」と明らかに同じではありません。どちらか一方を他方から導き出すことはできますか?

import 'reflect-metadata';


const MY_CLASS_DECORATOR_KEY = 'MyClassDecoratorKey';
const MY_PROPERTY_DECORATOR_KEY = 'MyPropertyDecoratorKey';

export const MyClassDecorator = options => {
    return function (target) {
      console.log('class target: ' , target);
      Reflect.defineMetadata(MY_CLASS_DECORATOR_KEY, options, target);
    };
};

export const MyPropertyDecorator = (options): PropertyDecorator => {
    return (target, property) => {
      console.log('property target: ' , target);
      const metadata = Reflect.getMetadata(MY_PROPERTY_DECORATOR_KEY, target) || {};
      metadata[property] = options;
      Reflect.defineMetadata(MY_PROPERTY_DECORATOR_KEY, metadata, target);
    };
};

@MyClassDecorator('my class decorator value')
class MyClass {
    @MyPropertyDecorator('first my property decorator value')
    myFirstProperty: any;

    @MyPropertyDecorator('second my property decorator value')
    mySecondProperty: any;
}

console.log('keys: ', Reflect.getMetadataKeys(MyClass));

出力に注意してください:

property target:  MyClass {}
property target:  MyClass {}
class target:  function MyClass() {
    }
keys:  [ 'MyClassDecoratorKey' ]

メタデータキーを取得して、プロパティデコレータからのキーも表示するにはどうすればよいですか?

10
bkinsey808

はい、デコレータで好きなことを自由に行うことができますが、あなたが知っているように、あなたの問題はあなたが通過しているターゲットにあります。

基本的に、プロパティデコレータでは、targetパラメータは、デコレータが静的プロパティとインスタンスプロパティのどちらで使用されているかに応じて、次の2つのいずれかになります。

静的プロパティでは、targetプロパティはクラスコンストラクタ関数になります。これは、静的プロパティでは、ターゲットがクラスデコレータの場合とまったく同じになることを意味します。

ただし、インスタンスプロパティでは、targetパラメータは、コンストラクタではなく、作成したクラスのprototypeになります。これが、あなたが見ている行動を見ている理由です。インスタンスプロパティの場合、クラスデコレータの場合のように、メタデータがconstructorにアタッチされていません。

ただし、constructorというプロパティに格納されているプロトタイプインスタンスを指定すると、コンストラクター関数に簡単にアクセスできるため、まだ希望があります。したがって、あなたの場合、これを行うことで、探している動作を取得できます。

export const MyPropertyDecorator = (options): PropertyDecorator => {
    return (target, property) => {
      var classConstructor = target.constructor;
      console.log('property target: ' , classConstructor);
      const metadata = Reflect.getMetadata(MY_PROPERTY_DECORATOR_KEY, classConstructor) || {};
      metadata[property] = options;
      Reflect.defineMetadata(MY_PROPERTY_DECORATOR_KEY, metadata, classConstructor);
    };
};

注:上記の変更はインスタンスプロパティでは機能しますが、静的プロパティでは機能しません。両方のタイプのプロパティを処理する必要がある場合は、targettarget.constructorのどちらを使用するかを決定するロジックを追加する必要があります。

9
dtabuenc