Search code examples
javascripttypescriptdynamic-function

TypeScript call function in dynamic (anonymous) function


I am trying to create a dynamic function in TypeScript which calls an already existing function like:

let dynamicFunction = new Function("existingFunction(\"asdf\");");

function existingFunction(name: string) {
    console.log(name);
}

While debugging in chrome dynamicFunction looks like this:

(function() {
existingFunction("asdf");
})

When I try to execute dynamicFunction, it says "Uncaught ReferenceError: existingFunction is not defined", which is no surprise because it's a different scope, but how can I actually call exisitingFunction inside dynamicFunction?

Any help would be greatly appreciated!

Edit:

to be more precise: I've got a typescript file which contains one module. This module exports a function which should return the created dynamic function. The created dynamicFunction is then used in another module which actually contains the exisitingFunction.

I've chosen this approach because I need to convert a given string to an executable condition, which will be executed many times.

For example: convert string "VALUE==1" to:

function () {
    return exisitingFunction("VALUE") == 1;
}

A short example of how it should look like:

parser.ts:

export module Parser {
   export function getFunction(expression: string) {
      // Calculating condition...
      let condition = "existingFunction(\"VALUE\") == 1;"
      return new Function(condition);
   }
}

condition.ts:

import { Parser } from "./parser";
class Condition {
    // getting the DynamicFunction
    private _dynamicFunction = Parser.getFunction("VALUE==1");

    someFunctionInsideCondition() {
       // Calling the DynamicFunction
       this._dynamicFunction();
    }
}

// Maybe this function should be somewhere else?
function existingFunction(name: string) {
    console.log(name);

    return 1;
}

I hope this explains my problem a little bit better.


Solution

  • From the Function documentation

    Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.

    so you'll have to pass existingFunction as an argument or define it in the global space.

    try with

    var existingFunction = function(name: string) {
        console.log(name);
    }
    

    Also have a look at eval which will give you access to the current scope ...

    --- Update

    After the question update and considering your comment about not wanting to use eval because of security concerns (with which i totally agree)

    The problem is that in the generated function's scope, this is undefined. Making your existingFunction part of the global scope is already a bad idea and between Typescript and the modules architecture doesn't seem possible at all.

    So why not passing a context to the generated function?

    This will allow you to control how much of your application to expose to the generated function, while giving it access to external methods.

    Something along the lines of:

    class Parser {
        static getFunction(expression) {
            let condition = new Function("context", "context.existingFunction(\"VALUE\") == 1;");
            return condition;
        }
    }
    
    class Condition {
        constructor() {
            this._dynamicFunction = Parser.getFunction("VALUE==1");
        }
    
        someFunctionInsideCondition() {
            // Calling the DynamicFunction
            this._dynamicFunction(this);
        }
    
        existingFunction(name) {
            console.log("hello " + name);
    
            return 1;
        };
    }
    
    
    
    let c = new Condition();
    c.someFunctionInsideCondition();

    Of course your context can be a different object instead of this, where you keep all your utility functions.

    I had to donwpile (compile it down, my own word) to es2015 to make the example run here, but I made it originally in Typescript and works fine