0

I want to pass a function string name to a function, so I can use it like this:

function processArrayObjectPropName<T extends IkeyOfObject>(items: T[], methodName: string = 'toLowerCase'): 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[key[methodName]()] = item[key];  //throw an error
    }
  }

  return items;
}

But, tsc give me this error:

Error:(38, 16) TS7015:Element implicitly has an 'any' type because index expression is not of type 'number'.

2 Answers 2

1

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;
}
Sign up to request clarification or add additional context in comments.

Comments

1

key is a string, methodName is also a string, the expression key[methodName] would be using a string to index another string which is why you get an error.

I can't quite figure out what you're trying to achieve. Copy all keys of the object to lowercase (or otherwise mangled) versions of those keys perhaps? That might be:

  item[(key as any)[methodName]()] = item[key];

Casting key to the any type allows you to subscript the string and therefore get at its methods indirectly.

2 Comments

I want to do this: "Foo"['toLowerCase']().
right so you do as the code example I gave and convert the string to any (or to an indexable object type if you prefer).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.