Can typescript property decorators set metadata for a class?

In typescript, is it possible to use a property decorator to set metadata for a class? Consider the code below. The โ€œtargetโ€ of the class decorator does not seem to match the โ€œtargetโ€ of the property decorator. Can I get one from the other?

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)); 

Pay attention to the conclusion:

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

How can I get metadata keys to also display keys from a property decorator?

+6
source share
1 answer

Yes, you can do whatever you want in your decorator, but as you know, your problems are related to the goal that you pass on.

Basically, in a property decorator, the target parameter can be one of two things, depending on whether the decorator is used for a static property or an instance property:

In the static property, the target property will be a class constructor function. This means that with a static property, the target will be exactly the same as for your class decorator.

However, in the instance property, the target parameter will be the prototype of the class you created, not the constructor. That is why you see the behavior that you see. In the case of an instance property, your metadata is not bound to a constructor , as is the case with your class decorator.

However, there is hope, because you can easily get to the constructor function specified by the prototype instance, since it is stored in a property called constructor . Thus, in your case, you can get the behavior you are looking for by doing the following:

 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); }; }; 

NOTE: the above change will work for instance properties, but not for static properties. If you need to handle both types of properties, you will need to add additional logic to determine whether to use target or target.constructor

+4
source

Source: https://habr.com/ru/post/1015040/


All Articles