When and how the decorator applies decorated classes from @angular packages

If I use a decorator in my class, it will be evaluated when importing the class. Here is a small example:

@NgModule({ ... }) export class BModule { ... } 

Transmitted as:

 var BModule = (function () { function BModule() { } BModule = __decorate([ <---------- decorators are applied here core_1.NgModule({...}) ], BModule); return BModule; }()); exports.BModule = BModule; 

However, when a module or any other decorator is used in @angular packages, the output is as follows:

 var HttpClientModule = (function () { function HttpClientModule() { } return HttpClientModule; }()); HttpClientModule.decorators = [ { type: _angular_core.NgModule, args: [{ ... },] }, ]; 

As you can see, decorators are not used here. They are simply stored in the decorators property. Why is this different from my code?

The reason I'm asking is because when I import decorated classes, I expect decorators to apply them, so using Reflect possible:

 const providers = Reflect.getOwnMetadata('annotations', BModule); 

However, this does not work with decorated classes from @angular packages.

+1
source share
1 answer

When angulat allows annotations, it has three parameters :

1) Direct API

 // Prefer the direct API. if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) { let annotations = (<any>typeOrFunc).annotations; if (typeof annotations === 'function' && annotations.annotations) { annotations = annotations.annotations; } return annotations; } 

We usually use this API when writing code in ES5.

 MyComponent.annotations = [ new ng.Component({...}) ] 

2) tcickle API symbol

 // API of tsickle for lowering decorators to properties on the class. if ((<any>typeOrFunc).decorators && (<any>typeOrFunc).decorators !== parentCtor.decorators) { return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators); } 

Thus, angular reads annotations from the @angular/(core|material...) libraries. angular compiles libraries this way because it helps optimize the package. For example, we don’t need to send decorators helpers like _decorate, __metadata , and the code will be executed faster.

To do this, angular uses tslib when creating the library by running tsc with the --importHelpers options https://github.com/angular/angular/blob/master/build.sh#L127 .

Angular stuff does the same https://github.com/angular/material2/blob/master/tools/package-tools/rollup-helpers.ts#L9-L11

 // Import tslib rather than having TypeScript output its helpers multiple times. // See https://github.com/Microsoft/tslib 'tslib': 'tslib', 

3) Using Reflect

 // API for metadata created by invoking the decorators. if (this._reflect && this._reflect.getOwnMetadata) { return this._reflect.getOwnMetadata('annotations', typeOrFunc); } 

This API is used when we use metadata emitted by typescript.

To make sure that you get the metadata correctly, you can use a function, for example:

 declare let Reflect: any; function getAnnotations(typeOrFunc: Type<any>): any[]|null { // Prefer the direct API. if ((<any>typeOrFunc).annotations) { let annotations = (<any>typeOrFunc).annotations; if (typeof annotations === 'function' && annotations.annotations) { annotations = annotations.annotations; } return annotations; } // API of tsickle for lowering decorators to properties on the class. if ((<any>typeOrFunc).decorators) { return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators); } // API for metadata created by invoking the decorators. if (Reflect && Reflect.getOwnMetadata) { return Reflect.getOwnMetadata('annotations', typeOrFunc); } return null; } function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] { if (!decoratorInvocations) { return []; } return decoratorInvocations.map(decoratorInvocation => { const decoratorType = decoratorInvocation.type; const annotationCls = decoratorType.annotationCls; const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : []; return new annotationCls(...annotationArgs); }); } const annotations = getAnnotations(AppModule); 

Update:

The API for metadata created when decorators were called was changed to 5.0.0-beta.

 const ANNOTATIONS = '__annotations__'; // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) { return (typeOrFunc as any)[ANNOTATIONS]; } return null; 
+3
source

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


All Articles