10

I have a *.ts file with lots of exported functions like:

export function newItemFoo() {
    return {foo: 0, bar: '', baz: true};
}

export function newItemBar() {
    return {foo: 0, bar: ''};
}

export function newItemXXX() {
    return {xxx: 0};
}

I want to make a "magic method" (in the same *.ts) that can call one of those methods by name, eg:

export function magicMethod(name: string) {
    const newEmptyFunc = new Function(`return new${name}()`);
    return newEmptyFunc();
}
magicMethod('ItemFoo');

but it raise an error

Error: newItemFoo is not defined

It works with eval:

export function magicMethod(name: string) {
    return eval(`newEmpty${name}()`);
}
magicMethod('ItemFoo');

How do I call a function by string name with new Function()?

1 Answer 1

21

Both using eval() and new Function() are convoluted ways to achieve the goal.

Try a simpler approach . Create an object that provides a mapping between the values you want to pass to magicMethod() and the functions to be called. Then implement magicMethod() this way:

function newItemFoo() {
    return {foo: 0, bar: '', baz: true};
}

function newItemBar() {
    return {foo: 0, bar: ''};
}

function newItemXXX() {
    return {xxx: 0};
}

const magicWand: { [K: string]: Function } = {
   Foo: newItemFoo,
   Bar: newItemBar,
   xxx: newItemXXX,   // this allows you to use a different value for the argument
   yyy: newItemXXX,   // ... to use multiple names for the same function
                      // ... and to handle gracefully the calls of non-existing functions 
};

export function magicMethod(name: string) {
  if (magicWand[name]) {
    return magicWand[name]();
  }

  throw new Error(`Method '${name}' is not implemented.`);
}

Usage:

magicMethod('Foo')
// { foo: 0, bar: '', baz: true }
magicMethod('Bar')
// { foo: 0, bar: '' }
magicMethod('xxx')
// { xxx: 0 }
magicMethod('yyy')
// { xxx: 0 }
magicMethod('zzz')
// Error: Method 'zzz' is not implemented.

However, if you provide such a mechanism to invoke the functions defined by the module you probably don't want to make them public using the standard export mechanism but only though magicMethod(). It is the only function that needs to be export-ed.

Sign up to request clarification or add additional context in comments.

5 Comments

if you want to map methods inside a class, you need to add "this" for example: Foo: this.newItemFoo,
... and how could you do it if the functions were in different files?
@redevill import them into the file that defines magicMethod().
This doesn't really answer the question, the suggestion is a good alternate approach, however I specifically looked this up because I DON'T want to have to maintain a map of all my functions.
@zing The question asks for a quick & dirty solution to overcome a minor annoyance that seems important to the OP at the moment. The answer tries to coach the OP to write code that they can understand at a glance when they come over it months or years later, when the details of how the magic (read "quick and dirty") has been implemented vanished from the author's memory long time before. The code should nou only do the right thing but also say the right thing. Of course, everyone is free to choose the path they 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.