Features of ES6 Part 8: Tagged Templates

Tuesday, September 30, 2014

In a previous post we looked at template literals.

You might have glanced at the code in that post and thought: “gee, that’s useful”. Or, perhaps you are more the skeptical, security conscious type who thought “gee, new and easy ways to create injection vulnerabilities”.

Regardless of your initial take, an even more powerful feature in ES6 is the tagged template literal.

The tag is a function with the ability to interpret and process a template. The tag appears in front of the template, so in the following code the tag is upper.

var result = upper `${x} + ${y} is ${x+y}`;

The runtime will invoke the tag function and pass as a first argument an array containing the individual pieces of literal text from the template.

let test = function(literals, ...values) {
    expect(literals.length).toBe(2);
    expect(literals[0]).toBe("Hello, ");
    expect(literals[1]).toBe("!");
    return "test";
};

let name = "Scott";
let result = test `Hello, ${name}!`;
expect(result).toBe("test");

The template in the above code, `Hello, ${name}!`, generates an array of two values. The values contain only the literal text and no placeholders. The runtime will pass the evaluated expressions from any placeholders as individual arguments after the first array argument. It will be a common practice to capture the values using a rest parameter. Here is another code snippet showing all of the literals and values passed to the tag function.

let test = function(literals, ...values) {
    expect(literals.length).toBe(3);
    expect(literals[0]).toBe("Hello, ");
    expect(literals[1]).toBe(", ");
    expect(literals[2]).toBe("!");
    
    expect(values.length).toBe(2);
    expect(values[0]).toBe("Allen");
    expect(values[1]).toBe("Scott");
    
    return "test";
};

let firstName = "Scott";
let lastName = "Allen";
let result = test `Hello, ${lastName}, ${firstName}!`;

expect(result).toBe("test");

The general idea is that a tag function can build the result of a tagged template by concatenating strings. For each literal value, append the literal to the result and then append a value. However, a tag can interpret a template using any logic it desires, so if the tag simply wants to heedlessly return the string “test” regardless of the contents of the template, it can (as the code above is doing).

A more reasonable implementation of a tag might look like the following.

let upper = function(strings, ...values){
    let result = "";

    for(let i = 0; i < strings.length; i++){
        result += strings[i];
        if(i < values.length){
            result += values[i];
        }
    }

    return result.toUpperCase();
};

var x = 1;
var y = 3;
var result = upper `${x} + ${y} is ${x+y}`;

expect(result).toBe("1 + 3 IS 4");

The tag upper builds a result that would look just like the normal evaluation of the template literal, but uses toUpperCase before returning the result to ensure the result uses all uppercase letters.

The Significance of Tagged Template Literals

A tag’s ability to modify the output of a template is powerful. Imagine a tag that can build safe URLs by URL encoding a result, or a safehtml tag to build encoded HTML. Since JavaScript is often used to generate content (HTML, JSON, URLs, form encoded inputs, etc), a tag library that makes content generation safe and easy will be invaluable.

Even more interesting is the idea that the content inside of a template could be a language unto itself. The tag function could be the gateway to the interpreter for a domain specific language, which opens up many possibilities for the use (and abuse) of tagged template literals.

Want more? Watch JavaScript Fundamentals for ES6 on Pluralsight!


Comments
gravatar Kevin Hakanson Tuesday, September 30, 2014
Interesting. I've been working through some aspects of globalization recently, so my first thoughts were if the following would work? let en = `Hello, ${lastName}, ${firstName}!`; let es = `¡Hola, ${lastName}, ${firstName}!`; let result_en = test en; let result_es = test es; Also, is it a language feature, or can I source the template literals from an external source / String variable?
gravatar scott Tuesday, September 30, 2014
@Kevin: I believe getting the literals from an external source is a bit of a problem, but check this out: http://jaysoo.ca/2014/03/20/i18n-with-es6-template-strings/
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!