32

Say I have a variable str

var str = "123"

Now I could do console.log(`Hello ${str}`) and it will print Hello 123

Now I have another variable strnew

var strnew = 'Hello ${str}'

Note (based on answers/comments) - strnew is read from a file, so its always a string and cant be replaced with `

How do I console.log(...) to print Hello 123

Is it possible wihtout any kind of eval()

8
  • 2
    Possibly related: stackoverflow.com/q/40235363/476
    – deceze
    Commented Dec 13, 2016 at 9:43
  • 1
    I think the best way in this case is to use function, that accepts str and uses this template string inside. Commented Dec 13, 2016 at 9:46
  • I think this is just example, if you have very long string with multiple concatenations - template string is better. Commented Dec 13, 2016 at 9:53
  • If string is from file maybe it is better to use _.template instead, or some other template engine. I don't see any option to generate template string from string. Commented Dec 13, 2016 at 9:57
  • use regex replace like strnew.replace(/\$\{(.*?)\}/g,()=>window[RegExp.$1]) if you are not using scopes
    – Josh Lin
    Commented Dec 13, 2016 at 9:58

4 Answers 4

36

With something as simple as ${str} you can use a simple string replacement:

var template = (tpl, args) => tpl.replace(/\${(\w+)}/g, (_, v) => args[v]);

var tpl = 'Hello ${str} and ${other}';

console.log(template(tpl, {str: 'foo', other: 'bar'}));

In a general case, no, not possible without eval (short of writing your own js interpreter), because ${...} can contain arbitrary expressions.

For completeness' sake, here's the eval solution:

var template = function(tpl, args) {
    var keys = Object.keys(args),
        fn = new Function(...keys, 
          'return `' + tpl.replace(/`/g, '\\`') + '`');
    return fn(...keys.map(x => args[x]));
};


function test() {
    var myTpl = 'Hello ${str + "!"} and ${other.toUpperCase()}';
    console.log(template(myTpl, {str: 'foo', other: 'bar'}));
}

test();

4
  • I'm very sorry, I edited your post instead of suggesting an edit, but I reverted it. new Function('', 'return ' + tpl + ''); could be optimized to new Function('return ' + tpl + '');
    – Lyubomir
    Commented Dec 13, 2016 at 10:30
  • Great answer!! This should be the accepted answer. Give me a day or two to confirm. Commented Dec 13, 2016 at 11:07
  • 1
    template(myTpl, str='foo', other='bar') this code implicitly creates two global variables. Argumants passed to the function are never used.
    – Qwertiy
    Commented Sep 26, 2017 at 12:13
  • 1
    @Qwertiy: right-o, too much python recently ;) fixed.
    – georg
    Commented Sep 26, 2017 at 12:24
1

You can use function instead of just string.

var strnew = function(str){
  return `Hello ${str}`;
}
var str = "123";
console.log(strnew(str))

4
  • 2
    IMHO the best solution and probably the way template literals were intended to be used.
    – nils
    Commented Dec 13, 2016 at 10:05
  • Template strings are read from a file, they are not in the OP's code.
    – georg
    Commented Dec 13, 2016 at 10:05
  • @georg yes, he added this note later, I wrote comment about this. Commented Dec 13, 2016 at 11:46
  • yes. based on answers, i had to adjust my question. thanks @dm Commented Dec 13, 2016 at 12:43
0

Thanks to this answer, here is a bit of black magic code that achieves what you want. Disclaimer - this is for fun / very limited and exotic application. It is likely to be very slow and break down on lots of edge cases, but with a limited scope of your question it works.

function getString(){
	return "calculating ${foo} + ${bar} = ${foo + bar}";
}

var localEnvironmentProxy = new Proxy({}, {
    has(target, prop) { return true; },
    get(target, prop) { return (prop in target ? target : window)[prop]; }
  });
  
  with(localEnvironmentProxy){
  
	var foo = 1;
	var bar = 2;
  
	var templString = getString();
	
	var fnFullText = 'with(arguments[0]){ return `' + templString + '`;}';
	
	var tempalteFn = new Function(fnFullText);
				
	console.log(tempalteFn(localEnvironmentProxy));
    //calculating 1 + 2 = 3
  
  }

0

I built off of @georg's answer above for a typescript solution:

public renderTemplate(templateStr: string, args: any): string {
  templateStr = templateStr.replace(/`/g, '\\`');
  
  const keys = Object.keys(args);

  const fn = new Function(...keys, 'return `' + templateStr + '`');

  return fn(...keys.map(key => args[key]));
}

The usage is virtually the same as well.

Not the answer you're looking for? Browse other questions tagged or ask your own question.