• July 3, 2020

One String to Rule Them All

String literals are quite a common feature in most code—from error messages, to exception text, to trace data, to prompts and informational text—it’s rare to find a significant span of code that doesn’t contain string literals. JavaScript is, of course, no exception and historically a JavaScript coder had two options for string literals: putting them inside double quotes or single quotes. “I am a little teapot” or ‘I am a little teapot.’

The traditional JavaScript string literals work fine, but have some issues. One obvious issue is that it’s a bit of a problem if the enclosing quote should appear in the enclosed text. So ‘I’m a little teapot’ is a bit of a problem. Note here that JavaScript (like most programming languages) makes no distinction between a single quote and an apostrophe. One solution is to enclose such literals in the type of quotation marks not in the text: “I’m a little teapot.” For this reason, there seems to be a general tendency among developers in any language that provides both type of quotes to prefer double quotes, as these tend to occur more rarely in text—apostrophes are not that rare in literal text. Still, sometimes double quotes do appear: “I am a “little” teapot.” So in this case you would use ‘I am a “little” teapot.’ But now suppose you have ‘I’m a “little” teapot.’ Now what? The solution is, of course, to “escape” contained quotes that match the containing quotes: ‘I\’m am a “little” teapot.’ And life is good.

The ECMA 2015 JavaScript standard introduced a new type of string literal that was originally called “template strings” but is now called “template literals.” The two things that jump out when one first looks at template literals are:

  • The enclosing quote is a back-tick, aka grave accent: `
  • They can contain expressions

The first bulleted item immediately made me realize that template literals are a nice solution to the problem of string literals with both single and double quotes: ` I’m am a “little” teapot.` Sweet. In theory, the back-tick has the same problem as the other two type of quotes but the reality is that back-ticks almost never appear in any literal text, so the problem of nested quotes essentially goes away with template literals.

But I also quickly realized that template literals are useful when I’m building up a string with a mix of literals and variables or expression results:

throw Error("Call: " + functionName + " returned an error code of " + error);

which with a template literal I could code as

throw Error(`Call: ${functionName} returned an error code of ${error}`);

Not only is the template literal version shorter, I think few people would argue it’s not easier to read. Experience has shown that template literals with embedded functions are almost always shorter than the string concatenation versions.

So, having learned about template literals, I gradually started using them where I thought they were “needed,” but everywhere else I used traditional string literals. And slowly, but surely, I found myself using template literals more and more until one day it hit me: “Why am I bothering to use any other kind of string literals?” And I simply could not come up with a good answer. So now, when coding a literal string in JavaScript, I do not waste any mental bandwidth deciding what kind of literal string I will code, I simply use a literal template. Always. Full stop.

I’ll admit that part of my initial reticence in using template literals was that I found the back-tick ugly, and harder to visually pick up than single or double quotes. But both these issues were more a matter of training my eye (or more accurately the visual centers of my brain) than absolutes, and as I used template literals more and more, I found them no more problematic than say the period, which in theory is pretty hard to pick up, but which we’ve all trained our brains to pick up very easily.

So, by the time the realization hit me that there was no good reason not to use template literals instead of traditional quoted strings, my subconscious was completely onboard. Like Scarlett O’Hara, I’d never go “hungry,” again—I would go `hungry.`

If that was all there was to it, it would be sufficient to support the argument that one should always use template literals instead of traditional string literals. But there’s more.

Another advantage of template literals over traditional string literals is that they can span multiple lines, with the new-line character treated as part of the string literal. With traditional string literals, you might do something like:

console.log("Available options are:\n"
+ "-s   Run in silent mode\n"
+ "-t   Display timestamps\n"
+ "-v   Run in verbose mode");

This sort of text is most likely in a Node.js application. The “\n” is required to produce a line break (new line) and we have to use a plus instead of comma because otherwise console.log would add a space after the new line character so each line would be indented one character. One could solve this by putting the “\n” in front of each new line but that’s pretty ugly and not great for readability.

In any case, one can code the above using template literals as:

console.log('Available options are:
-s   Run in silent mode
-t   Display timestamps
-v   Run in verbose mode`);

Obviously much nicer but…the astute reader will notice something amiss with indentation. The problem is of course that template literals consider any leading blanks in new lines as part of the literal string. The above gets truly ugly if inside of some nested code:

options.forEach(
(option) => {
if (option == “-h”) {
console.log(`Available options are:
-s   Run in silent mode
-t   Display timestamps
-v   Run in verbose mode`);
return;
}
if (option == “-s”) {
…

Yuck. However, there is a solution to this problem: tagged templates. Tagged templates are simply template literals preceded by an unquoted string that is the name of a function that is passed the template string. Even better, that function with the results of expressions embedded in the template string before they’ve been converted to a string. To illustrate how this would work, let’s assume there’s a function called text and we do:

options.forEach(
  (option) => {
    if (option == “-h”) {
      text`
        Available options are:
        -s   Run in silent mode
        -t   Display timestamps
        -v   Run in verbose mode
      `;
      return;
    }
    if (option == “-s”) {
      …

Much nicer. The text function would simply scan its input and assume that leading blanks on the first line after a new line is the intended indentation, and that number of leading blanks is stripped from every subsequent line in the template literal. The text function would simply invoke console.log for each line after leading blank stripping.

How one might write a tagged template function is a full blog post in itself, but because I’m sure many readers are chomping at the bit to write one themselves, I’ll cover this topic in a future post after you’ve tried writing one yourself. Hint: it’s not a lot of code. And of course, you can find plenty of help on the internet by searching for “template literals.”

As a final exercise for the reader and as an example of the power of template literals, I’ll demonstrate a limitation of template literals and how a tagged template function can fix it. Consider the following code that logs the value of an object:

  console.log(“The options object contains:”, options);

Assuming options is a simple object, you might see something like:

  The options object contains: {verbose: true, maxLines: 99, prefix: “ABC”}

But you’ve bought into template literals so instead you code:

  console.log(`The options object contains: ${options}`);

and are rewarded with

  The options object contains: [object Object]

Not good. This is because console.log has some clever code to format objects nicely while the default toString for most objects simply provides a cursory summarization as seen above. One can get the same or similar functionality with template literals by using console.format under Node.js or JSON.stringify on a browser:

  console.log(`The options object contains: ${console.format(options)}`);

  console.log(`The options object contains: ${JSON.stringify(options)}`);

But this is a pain and is more work than letting console.log do the work for you. However, one can write a tagged template function called say consoleLog that would be called like:

  console.Log`The options object contains: ${options}`;

The tagged-template function could simply let console.format format any embedded expression results, providing a more concise version of console.log. Again, writing such a function is left as an exercise for the reader. Beyond being more concise than console.log, such a tagged-template function would have the further advantage that it solves the annoyance of console.log always putting a blank between its arguments—while this is usually what one wants, sometimes it’s not. For example:

  console.log(‘Invalid keyword “’, keyword, ‘”’);

Would have a blank after the first displayed double quote and before the second one, not what you want. The typical old-school fix is:

  console.log(‘Invalid keyword “’ + keyword + ‘”’);

which you may as well do with a template string:

console.log(`Invalid keyword “${keyword}”`);

or

consoleLog`Invalid keyword “${keyword}”`;

A later blog entry will provide an example implementation of consoleLog.

The bottom line is that there is no reason for anyone to use traditional literal strings in JavaScript, anymore. One should always use template literals which can do everything traditional literals strings can do but better, and a whole lot more. JavaScript template strings are truly the one string to rule them all.

Alex Kodat

Alex Kodat 1 Posts

Alex is a senior Rocket Model 204® developer, and Distinguished Engineer at Rocket Software.

0 Comments

Leave a Comment

Your email address will not be published. Required fields are marked *