In our programming project, we have a hierarchy of classes where some classes inherit from a base class.
Our goal is to create an instance method that is strongly-typed in such a way that it only accepts the name of another instance method as input.
We decided to implement this method in the base class so that all derived classes can inherit it. However, we are facing challenges in correctly typing this method.
After brainstorming, we came up with a solution using keyof this
:
abstract class Animal {
abstract species: string;
breathe() {
console.log(`Ahh`);
}
exec(methodName: keyof this) {
(this[methodName] as () => void)();
}
}
class Cat extends Animal {
lovesCatnip = true;
species = `felis catus`;
meow() {
console.log(`Meow`);
}
}
class Dog extends Animal {
chasesCars = true;
species = `canis familiaris`;
bark() {
console.log(`Woof`);
}
}
const fido = new Dog();
fido.exec(`breathe`); // Good
fido.exec(`bark`); // Good
fido.exec(`species`); // Bad
fido.exec(`chasesCars`); // Bad
const snowball = new Cat();
snowball.exec(`breathe`); // Good
snowball.exec(`meow`); // Good
snowball.exec(`species`); // Bad
snowball.exec(`lovesCatnip`); // Bad
You'll notice our comments indicate that although exec
uses keyof this
, it currently allows any instance property instead of just instance methods.
Previously, we raised a similar issue and found a workaround when exec
was in the same class as the methods. However, the solution doesn't work when exec
is part of a superclass.
Can you help us figure out how to type exec
so that it only accepts names of instance methods when called on a subclass?