33

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 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 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();

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

4 Comments

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 + '');
Great answer!! This should be the accepted answer. Give me a day or two to confirm.
template(myTpl, str='foo', other='bar') this code implicitly creates two global variables. Argumants passed to the function are never used.
@Qwertiy: right-o, too much python recently ;) fixed.
1

You can use function instead of just string.

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

4 Comments

IMHO the best solution and probably the way template literals were intended to be used.
Template strings are read from a file, they are not in the OP's code.
@georg yes, he added this note later, I wrote comment about this.
yes. based on answers, i had to adjust my question. thanks @dm
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
  
  }

Comments

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.

Comments

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.