I would like to create a mixin, that can use a mehod of the class into which it was mixed. However this is not working.
////////////////////
// Simple class
class User {
sayHello(msg:string){
console.log(msg)
}
}
// Needed for all mixins
type Constructor<T = {}> = new (...args: any[]) => T;
////////////////////
// A mixin that adds a method that uses a base class method
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
activate() {
(Base as unknown as User).sayHello("activated")
}
};
}
////////////////////
// Using the composed class
////////////////////
const ActivableUser = Activatable(User)
const activableUser = new ActivableUser()
activableUser.activate()
Run fails with [ERR]: Base.sayHello is not a function
.
Is there any way I can create a mixin that has access to base class methods and properties?
CodePudding user response:
Base
in the activate
method is the class constructor. It is not an instance. And activate
is an method on instances.
But since activate
is an instance method, you can just use this
instead.
Just replace Base
with this
and it works just fine.
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
activate() {
(this as unknown as User).sayHello("activated")
}
};
}
Additionally, you can get rid of that nasty cast through unknown
if you tell the mixin what instance methods it depends on.
function Activatable<
TBase extends Constructor<{ sayHello(msg: string): void }>
>(Base: TBase) {
return class extends Base {
activate() {
this.sayHello("activated")
}
};
}
CodePudding user response:
Fot completeness, based on @Alex Wayne's answer and his additional guidence in the comment, here is the solution I was really aiming for, namely to constrain the base class with an interface that it has to implement and require this interface from the base class in the mixin function.
////////////////////
// Interface that our base class has to implement
interface CanSayHello {
sayHello(msg:string):void
}
////////////////////
// Base class
class User implements CanSayHello {
sayHello(msg:string){
console.log(msg)
}
}
////////////////////
// Needed for all mixins
type Constructor<T = {}> = new (...args: any[]) => T;
////////////////////
// A mixin that adds a method that uses a base class method
function Activatable<TBase extends Constructor<CanSayHello>>(Base: TBase) {
return class extends Base {
activate() {
this.sayHello("activated")
}
};
}
////////////////////
// Creating the composed class
const ActivableUser = Activatable(User)
const activableUser = new ActivableUser()
////////////////////
// Using the composed class
activableUser.activate()