Angular calls ngOnInit only on components which have it build-time

Today I found a funny peculiarity in the ngOnInit handling in Angular.

Perhaps you’ve thought of setting ngOnInit dynamically on a component with a decorator like:

export function Magic(): PropertyDecorator {
   return function(target: any): void {
      target.ngOnInit = function(): void {
         console.log('Hello!')
      };
   };
}

Which will be used like this:

@Component({...})
export class SomeComponent {
   @Magic()
   foo: string;
}

Unfortunately, that won’t work with the AOT compiler on components without ngOnInit, like the one from the example.

The reason is that Angular core guards the ngOnInit call with code like this:

  if ((def.flags & NodeFlags.OnInit) &&
      shouldCallLifecycleInitHook(view, ViewState.InitState_CallingOnInit, def.nodeIndex)) {
    directive.ngOnInit();
  }

In other words, the ngOnInit we set with the decorator won’t be called without the NodeFlags.OnInit flag, emitted by the AOT compiler telling whether SomeComponent implements OnInit.

The Magic decorator will work in AOT only if you actually have an OnInit method:

@Component({...})
export class SomeComponent implements OnInit {
   @Magic()
   foo: string;

   ngOnInit(): void {
      // You don't need anything here.
   }
}

The solution? I just opted out from lifecycle hooks, relying on another approach :)