I have an object with some properties and methods. How do I give it a method that accepts only the name of another one of its methods?
For example, consider the command
method in this User
class. We want command
to accept the name of another method. Using keyof this
narrows the acceptable type to User
property names; how would we narrow it further to just User
method names, without hardcoding them?
class User {
nameFirst: string;
nameLast: string;
command(commandName: keyof this) {
this[commandName].call(this);
}
sink() {}
swim() {}
}
const alice = new User();
alice.command(`swim`); // Accepts `nameFirst` | `nameLast` | `command` | `sink` | `swim`; we want it to accept only `sink` and `swim`
3
Answers
To narrow the acceptable types of the command method to only User method names without hardcoding them, you can combine conditional and mapped types.
TypeScript doesn’t have a built-in
KeysMatching<T, V>
type operator to extract the keys of a typeT
whose values match a certain other typeV
, as requested in microsoft/TypeScript#48992, but you can implement one yourself that works for some use cases, for example:Then conceptually you’d make
commandName
of typeKeysMatching<User, ()=>void>
so that it only allows zero-arg methods. But this will cause a circularity warning (since the type ofcommandName
depends on the type ofUser
which depends on the type ofcommand
which depends on the type ofcommandName
). You can circumvent this by omitting"command"
from the keys to allow, using theOmit
utility type:Playground link to code
Not sure what you’re trying to do specifically but could this work for your use case?