I'm having trouble accessing a method in a hierarchy when each class contains a method with the same name.
class A {
constructor(private name: string) { }
notify() { alert(this.name) }
}
class B extends A {
constructor() {
super("AAA")
}
notify() {alert("B") }
}
class C extends B {
notify() { alert("C") }
callA() {
this.notify(); // this alerts "C"
super.notify(); // this alerts "B"
// How to call notify() of the class A so it alerts "AAA"?
}
}
new C().callA();
While I question the design that requires you to do this, you can easily acieve this by getting the original method of A.prototype
and using call
:
class C extends B {
notify() { alert("C") }
callA() {
A.prototype.notify.call(this);
}
}
Grandparent method can be reached by climbing prototype chain up:
class C extends B {
notify() { alert("C") }
callA() {
this.notify(); // this alerts "C"
const grandparentNotify = super.__proto__.notify;
grandparentNotify.call(this); // this alerts "AAA"
}
}
__proto__
is used for illustrative purposes, because the proper way to get object prototype is Object.getPrototypeOf
. Notice that super.__proto__
chain for grantparent prototype may be different between implementations (e.g. TypeScript and native).
Grandparent method shouldn't be reached, because this indicates design problem; a grandchild shouldn't be aware of grandparent methods. The use of call
in methods is another sign that class design went wrong.
If there's a need to use a method from another class (it doesn't really matter whether it is grandparent) in extended class, this should be done explicitly, via a mixin. Since C
doesn't need all grandparent methods and needs to avoid naming collisions, a method should be assigned directly:
interface C {
grandparentNotify(): void;
}
class C extends B {
notify() { alert("C") }
callA() {
this.notify(); // this alerts "C"
this.grandparentNotify(); // this alerts "AAA"
}
}
C.prototype.grandparentNotify = A.prototype.notify;
The interfaces are merged, and grandparentNotify
is accepted as C
method by typing system. This way looks raw, but it is idiomatic way to assign a method.
A bit more smoother way that provides some overhead but requires no interface merging is a getter:
class C extends B {
notify() { alert("C") }
get grandparentNotify() {
return A.prototype.notify;
}
callA() {
this.notify(); // this alerts "C"
this.grandparentNotify(); // this alerts "AAA"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With