V8 adds template literals from JavaScript ES6.
Template literals: What are they?
It’s a shorthand way of using a template into which variables are substituted in a string. This allows for better reuse of string structures, and a few other goodies besides (like all V8 additions, it’s more than just syntactical spruce up)
Using templates
We’ll use the same data, which I won’t bother repeating in each example as in Apps Script V8: spreading and destructuring
const people = [{ firstName:'John', middleName: 'Eustace', lastName:'Smith', },{ firstName:'Jane', lastName:'Doe' }]; const [john, jane] = people const {firstName, lastName} = john
These are equivalent. The template allows the variables to insert their values into a string which has a placeholder set up to accept them. They differ from regular literals in that they use backticks ` ` rather than single (or double) quotes, and the template placeholder (if present) looks like this – ${somevariable}.
These statements have the same outcome.
Logger.log('Mr. ' + firstName + ' ' + lastName + '.') // using template literals Logger.log(`Mr. ${firstName} ${lastName}.`)
Evaluating expressions in templates
The placeholder can contain any expression, so you could do something like this to conditionally set content.
// Mr. John Smith has a middle name Logger.log(`Mr. ${firstName} ${lastName} has ${john.middleName ? 'a' : 'no'} middle name`) // Ms. Jane Doe has no middle name Logger.log(`Ms. ${jane.firstName} ${jane.lastName} has ${jane.middleName ? 'a' : 'no'} middle name`)
Multiline literals
Long strings in JavaScript are a nuisance. V8 allows you to spread strings over multiple lines of code.
const longText ='sometimes you have a really long piece of text to assign to a variable and you would like to put it on a new line' const longCut = 'so you end up ' + 'doing' + 'this' // now you can do this const longer = `now you can do this`
New lines in multline literals
Of course, you can insert new lines in strings using \n, but with V8 multiline strings, the new lines (and spacing) are preserved. That can be great if you are using, say GraphQL, as you can now copy queries directly from GraphQL without endless fiddling with quotes and \n.
// handy for graphql, where queries can be directly pasted in from graphiql const query = `{ Person(id:1) { id firstName lastName } }` // versus const whatapain = '{' + '\n' + ' Person(id:1) {' + '\n' + ' id' + '\n' + ' firstName' + '\n' + ' lastName' + '\n' + ' }' + '\n' + '}'
This became such a pain for me in Apps Script that I created some helper functions to do it for me, Formatting GraphQL queries.
Sometimes though, you don’t actually want to keep the extra spacing and new lines that are preserved inside backticked quotes, so you have to undo them again – perhaps like this.
// but the \n are preserved // so we can get rid of them again Logger.log(longer.replace(/\n/g,'')) // but we have also have the spacing in the code to get rid of them again Logger.log(longer.replace(/\n/g,'').replace(/\s+/g,' '))
More than just syntax
Let’s create a concise logger for the next bit, as we eventually want to have a look at the nature of what’s being logged.
const loga = lit => Logger.log(lit)
These are almost equivalent, but it looks like the backticked literal with no function brackets (which looks like very peculiar syntax) not only has some implied brackets to provoke function execution but also passes some kind of array to the function it calls
// hello loga('hello') // hello loga(`hello`) // this works, but it's actually an object that gets passed rather than a string // [hello] loga`hello`
Confirm it’s an array
const logb = lit => Logger.log(Array.isArray(lit)) // true logb`hello`
The template structure
So this implied function execution provoked by the backticks is the mechanism that enables the template processing in the first place. If you use gql in GraphQL you may have been puzzled by the syntax (I certainly was), which looks something like this. What is happening here is that the backticks are simply provoking a call to the gql function, and passing something that looks a bit like a string to that function.
const query = gql`{ Person(id:1) { id firstName lastName } }`
But this also means that we can take a look inside the template by calling our own function using the behavior of the backtick. Templates used in this way are called tagged templates and allow you to do your own parsing and substitution.
First, make a logger to take multiple arguments
const logc = (...items) => Logger.log(items)
What actually gets passed in a tagged template is the string (as we’ve seen that’s an array), along with a value to fill each of the placeholders, and finally, another array which is the version of str (.raw) without escapes (such as \n) rendered. So let’s create a function to examine what that looks like.
// examine what is actually getting passed const examine = (str,...values) => logc( str, ...values, str.raw)
tagged template content
Here’s a sample of what you get back
// [[hello], [hello]] examine`hello` // [[hello , ], John, [hello , ]] examine`hello ${john.firstName}` // [[hello , , ], John, Smith, [hello , , ]] examine`hello ${john.firstName} ${john.lastName}` // [[hello , , // my cousin], John, Smith, [hello , , \n my cousin]] examine`hello ${john.firstName} ${john.lastName} \n my cousin`
Not only can you parse the templates this way, but you can also change their behavior! Let’s say, for example, that we wanted to look up a database using some of the values passed for evaluation. For simplicity, I’m looking up a list here, and omitting error checking and mitigation
const lookup = (str,...values) => { // the template should have these two things in it const [firstName, lastName] = values // find the middle name const {middleName} = people.find(f=>f.firstName === firstName && f.lastName === lastName) // redo the templating // tricky because we have to find the null values the templates were at // then add any values we havent used yet // so needs some clean up here const newStr = str.map(f=>f || values.shift()).concat(values) return `${newStr.join(' ')} - apparently your middle name is ${middleName || 'missing'}` }
Now we can customize populating the template, using the above function.
// hello John Smith - apparently your middle name is Eustace loga(lookup`hello ${john.firstName} ${john.lastName}`) // hello Jane Doe - apparently your middle name is missing loga(lookup`hello ${jane.firstName} ${jane.lastName}`
Summary
This is something I wish had been available when I started to use GraphQL with Apps Script. It would have saved lots of pain. Another great V8 feature.