Strictly better is a term I picked up from my tenure as a Magic* player. According to the definition, a Magic card is said to be strictly better than another when it’s better in some way but isn’t worse in any way.
Strictly better describes a card which is, in isolation from other effects, superior to another card in at least one respect, while being worse in zero respects.
* a fantasy trading card game, it’s kind of like a mix between chess and poker. Yeah.
When I say that template literals are strictly better than single and double quoted strings, I mean that they are superior in several respects while not being worse in any aspects. As noted in the definition, there’s always some situations in which a “strictly better” card could in fact be worse than the other, but these situations are atypical enough that we can get away with using the “strictly better” terminology on a figurative basis.
So how exactly are template literals strictly better than single and double quoted strings? And why haven’t they pervasively taken over single and double quoted strings, if that were the case?
Read on and find out.
Expression Interpolation
Assuming an ES6 codebase, there are several ways to do interpolation in strings.
You could use string concatenation, involving +
signs and a string for each piece of the template you’re working on. Using concatenation can quickly get out of hand when dealing with longer templates, not to mention reusability is hard unless you abstract away your template in a small function.
'Hello ' + name + '! It\'s a pleasure to greet you.'
You could use util.format
in Node. Here we have placeholders such as %s
which describes how the value is to be formatted, but doesn’t say anything about the value that should be placed in that socket in the template. This method – and similar ones – scales better than string concatenation, but it can be difficult to infer the context for each replacement value in the template without a small function that matches its arguments to each value for the template.
util.format('Hello %s! It\'s a pleasure to greet you.', name)
You could use string interpolation in template literals. Here you can inline JavaScript expressions such as expression
wrapped in ${expression}
. It’s nice to have a name for the slot, such as the name
for the person you’re greeting, placed directly within the template.
`Hello ${name}! It's a pleasure to greet you.`
Of course, nothing is stopping you from using template literals in the previous two incarnations of interpolation. Overlooking the fact that we should probably be just using expression interpolation within a single template literal, nothing stops us from using template literals in the exact same way as we used quoted strings.
`Hello ` + name + `! It's a pleasure to greet you.`
util.format(`Hello %s! It's a pleasure to greet you.`, name)
Template literals give us the choice to interpolate inline in the string, if we want to, while quoted strings don’t. As an added bonus, interpolation could be several levels deep: any expressions – including other template literals – can be used for interpolations.
Character Escaping
This one is fairly minor, but it’s still worth pointing out.
In single quoted strings, we need to escape single quotes using \'
.
'What\'s that?'
In double quoted strings, we need to escape double quotes using \"
.
"Hey, who's \"that\" are you referring to?"
In template literals, we need to escape backticks using \`
.
`Hey, programmers use backticks to render code in Markdown, like \`this\`.`
We also need to escape any dollar sign followed by an opening curly brace used to start template expressions. This can be done by escaping either the dollar sign, or the brace, or both. (It’s hard to think of cases where this sequence of two characters might find its way into a string literal. As an unlikely example, the random symbols below are called grawlix or “symbol swearing”.)
var message = `Check out this string literal example: \`Hello \${foo}!\`. It's #!$\{^% great!`
console.log(message)
// > Check out this string literal example: `Hello ${foo}!`. It's #!${^% great!
You’re far less likely to need backticks in your everyday strings than single or double quotes, which are commonplace in English and language in general. The sequence “${” is unlikely to be seen except in random character sequences. Using template literals, then, translates into less escape codes that can pollute your otherwise beautiful strings.
Multiline Strings
Making regular strings multiline can be annoying. We have a variety of options, though, for both single and double quoted strings.
Using an array and then concatenating with \n
is possibly my favorite approach. Here we need individual strings per line, and the surrounding array with its join, but there’s no escaping involved.
[
'# Ingredients',
'',
'- 2x Tomato',
'- 2x Lemon',
'- 300cc Vodka',
'- Pepper'
].join('\n')
An almost identical approach, that seems more typically favored by JavaScript developers, is to concatenate using +
. In this case we’d have to add our \n
ourselves. Typically, however, developers do not mind the lack of new lines because their multiline string is used to declare a piece of HTML for Angular and jQuery applications. Using +
is a bit noisier than using ,
, and we’d have to hand-roll our \n
, but it does allow for us to use a single string. Even though inline HTML is far from ideal, avoiding new lines is hardly a problem on top of that.
'<div>' +
'<span>Hello </span>' +
'<span>' +
name +
'</span>' +
'</div>'
An approach you hardly ever see is using \n
new line escapes followed by an escape \
that allows you to continue writing the same string on a new line. This is noisy on the right hand side, but not as noisy on the left hand side, but it does allow for us to use a single string.
'# Ingredients\n\
\n\
- 2x Tomato\n\
- 2x Lemon\n\
- 300cc Vodka\n\
- Pepper'
Plus, if you don’t need the \n
escapes, – as is the case with HTML strings – you can end up with quite decent looking strings.
'<div>\
<span>Hello </span>\
<span>' +
name +
'</span>\
</div>'
Wait, that didn’t look so great. That’s because as soon as we have some sort of interpolation in the string, we’re back to concatenating, and it can get awful.
Then there’s other less frequently surfaced approaches, such as using a comment in a function and a library to extract the comment into a multiline string. Here’s an example using Sindre’s multiline
package. This approach only has noise on the beginning and end of the declaration, but not within, which is why it can be preferred. It does involve a third party package and a convoluted hack just to render multiline strings, which some people could take issue with.
multiline(function(){/*
<!doctype html>
<html>
<body>
<h1>Pony Foo</h1>
</body>
</html>
*/});
When it comes to template literals, multiline support is included by default. The following example, using template literals, is equivalent to the previous one where we used multiline
.
`<!doctype html>
<html>
<body>
<h1>Pony Foo</h1>
</body>
</html>`
If you have expressions you need to interpolate into your multiline strings, template literals aren’t shaken up like other approaches.
`<!doctype html>
<html>
<body>
<h1>${title}</h1>
</body>
</html>`
Plus, if you’re worried about XSS and you’d like to HTML-encode interpolations, you could use tagged templates that automatically do this for you. The following example uses an encode
tagged template to escape interpolations in an HTML string using a brittle escape
implementation.
function encode (template, ...expressions) {
const escape = text => text
.replace(/&/g, `&`)
.replace(/"/g, `"`)
.replace(/'/g, `'`)
.replace(/</g, `<`)
.replace(/>/g, `>`)
return template.reduce((result, part, i) => {
return result + escape(expressions[i - 1]) + part)
})
}
const title = `<script src='https://malicio.us/js'></script>`
encode`<!doctype html>
<html>
<body>
<h1>${title}</h1>
</body>
</html>`
The resulting string is plain HTML, where interpolated expressions have been HTML encoded.
<!doctype html>
<html>
<body>
<h1><script src='https://malicio.us/js'></script></h1>
</body>
</html>
Again, you have the option of doing all of this, but nothing impedes you from applying any of the previous approaches to template literals.
But, JSON.
The JSON argument is bound to come up, but we can shrug it off as a weak one. In JSON, strings – and even property keys – must be double quoted.
{
"key": "value"
}
You can’t use single quotes to declare strings in JSON, yet they are typically favored over double quotes when it comes to JavaScript code. The same argument can, by transitive property, be applied to template literals: you can’t use template literals to declare strings in JSON, yet they should be favored over single or double quotes.
Choose Your Weapon
Here’s a Twitter poll I ran last week, asking about whether people are using template literals as their default string representation of choice.
Half of respondents still use single quotes, where close to half claim to be prefer template literals. Some replied to my tweet saying that they use template literals when they explicitly need interpolation or multiline support, but otherwise fall back to single quoted strings.
Using backticks is better because you rarely need to escape backticks \`
or \${
, you get multiline support, and you get variable interpolation. Many admitted they’d switch over from single quoted strings to template literals when interpolation or multiline support was required, so why not switch everything over by default and take advantage of the lower amount of escapes?
The only valid argument I’ve read against using template literals, much like with const
, is that people are used to single quoted strings. While familiarity is a fair claim, you can conjure up the habit of using template literals very quickly, as we’ll explore next.
Using eslint
: the quotes
Rule
ESLint is a modern linter for JavaScript code bases. If you’re still on another linter, I strongly encourage you to check it out.
The "quotes"
rule tells ESLint that string literals must be declared using backticks, or template literals, and that otherwise an error should be reported. This way we can ensure that, going forward, we’re using template literals across our codebase.
"quotes": ["error", "backtick"]
“But, Nico” – you may object – “a rule like this would report hundreds of stylistic errors in my code base, and I have no time to fix all of that.”. You’d be absolutely right.
ESLint ships with a --fix
flag for its CLI, where it’ll attempt to resolve stylistic issues, before reporting them as hard errors. If an stylistic issue can be fixed, then --fix
will take care of it for you without ever complaining about it. As it turns out, the quotes
rule is an stylistic choice that is easily fixed with --fix
.
The following command would turn all your strings into template literals, provided you’ve set up the quotes
rule to "backtick"
.
eslint --fix .
Ta-da!
You may find your code base now has bits like the following, instead of the single quoted strings you previously had.
`Hello ` + name + `!`
You can gradually upgrade code like the above into interpolated expressions over time, getting to a more consistent code base. There’s no excuse, however, to get away with not using template literals!
Quick Disclaimer:
'use strict'
DirectivesNote the
'use strict'
directive won’t be executed if wrapped in backticks instead of single or double quotes. Whileeslint --fix
doesn’t break'use strict'
statements, you could mistakenly do that by hand, so please avoid doing so!
Now go evangelize the use of template literals everywhere, so that I can sleep better at night.
This article is sponsored by the backtick conglomerate holding commonly referred to as “best practices”.
Comments