Murolem
Murolem

Reputation: 821

What signature should I specify in this generics mess?

What signature should ctx argument of loggerOfHidden function have for it to work?

What 'T' could be instantiated with an arbitrary type which could be unrelated to '(Anonymous class)' error means?

Ignoring errors it outputs just fine.

Argument of type 'this' is not assignable to parameter of type 'T & Constructor<ISayHidden>'.
  Type '(Anonymous class)' is not assignable to type 'T & Constructor<ISayHidden>'.
    Type '(Anonymous class)' is not assignable to type 'T'.
      'T' could be instantiated with an arbitrary type which could be unrelated to '(Anonymous class)'.
        Type 'this' is not assignable to type 'T'.
          'T' could be instantiated with an arbitrary type which could be unrelated to 'this'.
            Type '(Anonymous class)' is not assignable to type 'T'.
              'T' could be instantiated with an arbitrary type which could be unrelated to '(Anonymous class)'
type Constructor<T> = new (...args: any[]) => T;

interface ISayHidden {
    sayHidden(): void;
}

class Factory<T extends Constructor<Foo>> {
    constructor(public Base: T) {}

    ExtendSayHidden(loggerOfHidden: (ctx: T & Constructor<ISayHidden>) => void) {
        return new Factory<T & Constructor<ISayHidden>>(class extends this.Base implements ISayHidden {
            sayHidden() {
                loggerOfHidden(this);
            }
        });
    }
}

class Foo {
    hiddenMessage = 'Wow, you found me!';

    sayHi() {
        console.log('Hi!');
    }

    sayBye() {
        console.log('Bye!');
    }
}

const FooExtended = new Factory(Foo).ExtendSayHidden(ctx => {
    console.log(ctx.hiddenMessage);
}).Base;

let fooExtended = new FooExtended();

fooExtended.sayHidden();

EDIT: the future purpose of loggerOfHidden will be manupulating the extended instance that is being generated by ExtendSayHidden function. Note that Factory can and will contain a multitude of functions, each of which will extend current Base class further. I just want TypeScript to pretend that ctx is no more than Foo extended by the current class, and be able to access these field. I could create a more extensive example if needed.

EDIT2: Updated example. Can TypeScript provide ctx with the context of the current class?

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

interface ISayHidden {
    hiddenMessage: string;
    sayHidden(): void;
}

class Factory<T extends Constructor<Foo>> {
    constructor(public Base: T) {}

    ExtendSayHidden(loggerOfHidden: <U extends Foo>(ctx: U) => void) {
        return new Factory<T & Constructor<ISayHidden>>(class extends this.Base implements ISayHidden {
            hiddenMessage = 'Wow, you found me!';

            sayHidden() {
                loggerOfHidden(this);
            }
        });
    }
}

class Foo {
    sayHi() {
        console.log('Hi!');
    }

    sayBye() {
        console.log('Bye!');
    }
}

const FooExtended = new Factory(Foo)
.ExtendSayHidden(ctx => {
    console.log(ctx.hiddenMessage);
}).Base;

let fooExtended = new FooExtended();

fooExtended.sayHidden();

Upvotes: 0

Views: 189

Answers (1)

Mirco S.
Mirco S.

Reputation: 2610

The this inside your anonymous class refers to the object and not the constructor.

class extends this.Base implements ISayHidden {
    sayHidden() {
       loggerOfHidden(this);
    }
}

But you are typing it as constructor/class with T & Constructor<ISayHidden>. Thus you need to change the typing to refer to the instantiated object of class Foo (playground):

class Factory<T extends Constructor<Foo>> {
    constructor(public Base: T) {}

    ExtendSayHidden(loggerOfHidden: <U extends Foo & ISayHidden>(ctx: U) => void) {
        return new Factory<T & Constructor<ISayHidden>>(class extends this.Base implements ISayHidden {
            hiddenMessage = 'Wow, you found me!';

            sayHidden() {
                loggerOfHidden(this);
            }
        });
    }
}

Upvotes: 2

Related Questions