8

An Angular component has decorators:

@Component({ ... })
export class MyAngularComponent {
  @Input() myInputParam: MyType;
  @Input() myOtherInputParam: MyOtherType;
  @Output() myOutputParam: MyOtherOutputType;
}

I've got an Angular library where a lot of code repetitions could be avoided (and bundle size reduced) if I could programmatically retrieve Angular's @Input() decorators inside a given component class (that belongs to the library, though).

But I have doubts on the portability of such an implementation. I've read somewhere that the Reflect polyfill (needed to read decorators at runtime) isn't needed if the Angular app has been built with AoT enabled (and given that only Angular decorators are used). So I presume I can't just use Reflect.*. How does Angular stores the decorators? Is there a reliable, future-proof way to read them?

Minification shouldn't be a problem since that would be used to read decorators of library's components only, so I have control on this.

So, if that's doable in a portable way (or not, I'm still interested otherwise), how can I read those decorators?

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
Morgan Touverey Quilling
  • 4,181
  • 4
  • 29
  • 41

1 Answers1

12

I've read somewhere that the Reflect polyfill (needed to read decorators at runtime) isn't needed if the Angular app has been built with AoT enabled... How does Angular stores the decorators?

In fact, Angular plans to remove dependency on the Reflect object even in runtime. For that reason, in the newest v5 the Reflect.defineMetadata has been replaced with Object.defineProperty in the makeDecorator that is responsible for attaching the metadata to the class. Here is the relevant code:

export const ANNOTATIONS = '__annotations__';
export function makeDecorator(
    ...
    const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
      // Use of Object.defineProperty is important since it creates non-enumerable property which
      // prevents the property is copied during subclassing.
      const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
          (cls as any)[ANNOTATIONS] :
          Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS]; <-----
      annotations.push(annotationInstance);
      return cls;
    };

It means that in the v5 you can access decorators on the component class like this:

export class AppComponent {
    constructor() {
        console.log((<any>AppComponent).__annotations__);
    }

Is there a reliable, future-proof way to read them? I don't think there's anything future-proof with Angular.

When compiling an application using AOT Angular uses static code analysis and heavily relies on the AST produced by the TS compiler. If you're interested in accessing decorators in the build time I guess that is the way to go and I would call it the most future-proof solution.

yurzui
  • 205,937
  • 32
  • 433
  • 399
Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • 3
    @MorganTouvereyQuilling I also strongly suggest you to follow https://blog.angularindepth.com/ publication. There you can find more information about internals – yurzui Oct 26 '17 at 06:26
  • 1
    @MorganTouvereyQuilling, you're welcome. As yurzui suggested, there's a lot of unique and in-depth content at angularindepth.com publication – Max Koretskyi Oct 26 '17 at 06:48
  • @AngularInDepth.com is __annotations__ a API upon which we should rely? – Cec Apr 01 '18 at 22:01
  • 1
    @Cec, it's not public, so you can't really rely on anything private – Max Koretskyi Apr 02 '18 at 05:15
  • `__annotations__` is undefined in latest version `5.2.9`. Are there any other solutions? Thanks – tic Apr 11 '18 at 00:35
  • @tic, how do you access them? I've just checked and it works fine for me. – Max Koretskyi Apr 11 '18 at 05:23
  • 1
    I think something I didn't realize is that this doesn't work when you use `ng build --prod' as all the decorators are removed when transpiling. Is there any way to use this when building for production? – tic Apr 11 '18 at 21:54
  • @tic, I don't know, haven't looked at the AOT that close – Max Koretskyi Apr 12 '18 at 10:16
  • Regarding `__annotations__` being removed on `ng build --prod`. Metadata attributes are still accessible using `ComponentFactoryResolver`. Check out this [answer](https://stackoverflow.com/a/56158669/308645). – unitario Feb 15 '20 at 08:48