Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 tagged templates practical usability

I understand the syntax of ES6 tagged templates. What I don't see is the practical usability. When is it better than passing an object parameter, like the settings in jQuery's AJAX? $.ajax('url', { /*this guy here*/ })

Right now I only see the tricky syntax but I don't see why I would need/use it. I also found that the TypeScript team chose to implement it (in 1.5) before other important features. What is the concept behind tagged string templates?

like image 539
Gábor Imre Avatar asked Jul 23 '15 14:07

Gábor Imre


People also ask

What are the template literals in ES6?

Template literals are a new feature introduced in ECMAScript 2015/ ES6. It provides an easy way to create multiline strings and perform string interpolation. Template literals are the string literals and allow embedded expressions. Before ES6, template literals were called as template strings.

What are tagged templates in JS?

Syntactically, a tagged template is a template literal that follows a function (or rather, an expression that evaluates to a function). That leads to the function being called. Its arguments are derived from the contents of the template literal.

Why would you use a tagged template?

Tags allow you to parse template literals with a function. The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions. The tag function can then perform whatever operations on these arguments you wish, and return the manipulated string.


1 Answers

They're useful because the function can (almost) completely define the meaning of the text inside it (almost = other than placeholders). I like to use the example of Steven Levithan's XRegExp library. It's awkward to use regular expressions defined as strings, because you have to double-escape things: Once for the string literal, and once for regex. This is one of the reasons we have regular expression literals in JavaScript.

For instance, suppose I'm doing maintenance on a site and I find this:

var isSingleUnicodeWord = /^\w+$/;

...which is meant to check if a string contains only "letters." Two problems: A) There are thousands of "word" characters across the realm of human language that \w doesn't recognize, because its definition is English-centric; and B) It includes _, which many (including the Unicode consortium) would argue is not a "letter."

Suppose in my work I've introduced XRegExp to the codebase. Since I know it supports \pL (\p for Unicode categories, and L for "letter"), I might quickly swap this in:

var isSingleUnicodeWord = XRegExp("^\pL+$"); // WRONG

Then I wonder why it didn't work, *facepalm*, and go back and escape that backslash, since it's being consumed by the string literal:

var isSingleUnicodeWord = XRegExp("^\\pL+$");
// ---------------------------------^

What a pain. Suppose I could write the actual regular expression without worrying about double-escaping?

I can: With a tagged template function. I can put this in my standard lib:

function xrex(strings, ...values) {
    const raw = strings.raw;
    let result = "";
    for (let i = 0; i < raw.length; ++i) {
        result += raw[i];
        if (i < values.length) { // `values` always has one fewer entry
            result += values[i];
        }
    }
    return XRegExp(result);
}

Or alternately, this is a valid use case for reduce, and we can use destructuring in the argument list:

function xrex({raw}, ...values) {
    return XRegExp(
        raw.reduce(
            (acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
            ""
        )
    );
}

And then I can happily write:

const isSingleUnicodeWord = xrex`^\pL+$`;

Example:

// My tag function (defined once, then reused)
function xrex({raw}, ...values) {
    const result = raw.reduce(
        (acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
        ""
    );
    console.log("Creating with:", result);
    return XRegExp(result);
}

// Using it, with a couple of substitutions to prove to myself they work
let category = "L";                // L: Letter
let maybeEol = "$";
let isSingleUnicodeWord = xrex`^\p${category}+${maybeEol}`;
function test(str) {
    console.log(str + ": " + isSingleUnicodeWord.test(str));
}
test("Русский");  // true
test("日本語");    // true
test("العربية");  // true
test("foo bar");  // false
test("$£");       // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.2.0/xregexp-all.min.js"></script>

The only thing I have to remember now is that ${...} is special because it's a placeholder. In this specific case, it's not a problem, I'm unlikely to want to apply a quantifier to the end-of-input assertion, but that's a coincidence...

like image 182
T.J. Crowder Avatar answered Sep 21 '22 23:09

T.J. Crowder