TL;DR: Use the code snippet marked with Solution.
The error message makes sense, because when you're trying to run key[methodName], it actually calls String.prototype.operator[], with a signature of readonly [index: number]: string;.
As methodName here is a string and not a number, as required by its signature, it gives the warning message.
What we can do here is to make methodName more specific, so TypeScript knows that key[methodName] does not return null in any case.
function processArrayObjectPropName<T extends IkeyOfObject>
(items: T[], methodName: keyof String = 'toLowerCase'): T[] {
Now, TypeScript gives another warning, because number of arguments may be different for different functions.
[ts] Cannot invoke an expression whose type lacks a call signature. Type 'number | (() => string) | ((pos: number) => string) | ((index: number) => number) | ((...strings:...' has no compatible call signatures.
So we need to make the type specification of methodName even more specific, making sure it contains only functions accept 0 parameters;
In this way, we'll have something similar to this, which compiles:
// Solution 1
type StringMethodWithZeroParameter = 'toLowerCase' | 'toUpperCase'
function processArrayObjectPropName<T extends IkeyOfObject>
(items: T[], methodName: StringMethodWithZeroParameter = 'toLowerCase'): T[] {
Meanwhile there is an alternative to pass in lambda instead of function name, if the signature can be changed.
// Solution 2
processArrayObjectPropName(items, String.prototype.toLowerCase);
function processArrayObjectPropName<T extends IkeyOfObject>(items: T[], transformer: (i: string) => string): T[] {
for (const item of items) {
const keys: string[] = Object.keys(item);
let keyCount: number = keys.length;
while (keyCount--) {
const key: string = keys[keyCount];
item[transformer(key)] = item[key];
}
}
return items;
}