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.