angular: (AOT): Angular does not invoke lifecycle listeners if typescript mixins are used

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Assume we have base class, mixin and component class:

class Base implements OnInit {
    constructor(){}
    ngOnInit():void {}
}

type Constructor<T> = new(...args: any[]) => T;

function Tagged<T extends Constructor<{}>>(base: T) {
    return class extends base {
        value: string;
        constructor(...args: any[]) {
            super(...args);
            this.value= "test";
        }
    }
}

export class Component extends Tagged(Base) {
    public item:any;

    constructor()  {
        super();
    }
}

Component’s ngOnInit listener will not be called in AOT.

Workaround:

export class Component extends Tagged(Base) {
    public item:any;

    constructor() {
        super();
    }

   ngOnInit():void {
         super.ngOnInit();
   }
}

Expected behavior

ngOnInit should be called.

Environment


Angular version: 4.4.0-RC.0
TypeScript: 2.4.2

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 21
  • Comments: 15 (10 by maintainers)

Most upvoted comments

This is already fixed by Ivy. But not likely to be fixed on current view engine.

The issue is that that static reflector does not use the type checker of TypeScript. Currently, if there is an expression in the extends clause we ignore it. This means we have no idea that the base class implements onInit nor an easy way to calculate it as it would require us to ask the type-checker.

We avoid using the type-checker because we need to be able to produce factory functions before type checker or we would have to do two full type-checks.

We should consider recognizing the pattern function f<T extends Constructor<{}>>(base: T) { as meaning we should just ignore the f and treat its parameter as the base class.

However, this would mean that we would not catch things like,

function CoolInitMixin<T extends Constructor<{}>>(base: T) {
    return class extends base {
        constructor(...args: any[]) {
            super(...args);
        }
        ngOnInit() {
         // something cool.
       }
    }
}