Using TypeScript, I'd like to create a class instances of which have a number of methods, but can be initialized to automatically add several other methods, based off of one of the predefined ones. The base class has a props method, which takes an object, and does stuff with it:
class MyClass {
props(props: Record<string, any>) {
// Do something with passed props
}
}
I want to be able to pass in a list of method names on construction that are then used to dynamically add class instance methods which use this props method to do something. So:
constructor(propList: Array<string> = []) {
propList.forEach(propName => {
// Don't allow overwriting of existing methods
if (this[propName]) {
throw new Error(
`Cannot create prop setter for '${propName}'. Method with that name already exists!`
);
} else {
this[propName] = (value: any) => {
this.props({ [propName]: value });
};
}
});
}
When I try to do so, TS shows an error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'MyClass'. No index signature with a parameter of type 'string' was found on type 'MyClass'.ts(7053)
Is there a way to do this? Saw this semi-related question, but didn't feel applicable. Thanks! New to TS.
EDIT
I tried having the class implement an open interface:
interface CanBeExtended {
[key: string]: any;
}
class MyClass implements CanBeExtended {
// ...
but I think that is making the class itself, but not instances of the class, extensible. At any rate, it is not fixing the issue.
You can declare an index signature in a class, as long as it doesn't conflict with the types any other members (so if you have a props() method, you can't make the index signature [k: string]: number, for example, since props is a method and not a number). In your case you want to use the any type, which will definitely not conflict (although it's also not very type safe). So it looks like this:
class MyClass {
[k: string]: any; // index signature
constructor(propList: Array<string> = []) {
propList.forEach(propName => {
// Don't allow overwriting of existing methods
if (this[propName]) { // no error here anymore
throw new Error(
`Cannot create prop setter for '${propName}'. Method with that name already exists!`
);
} else {
this[propName] = (value: any) => { // no error here anymore
this.props({ [propName]: value });
};
}
});
}
props(props: Record<string, any>) {
// Do something with passed props
}
}
And your errors go away.