How to write String.raw() with normal JavaScript code

168 views Asked by At

According to MDN,

String.raw() is the only built-in template literal tag. It has close semantics to an untagged literal since it concatenates all arguments and returns a string. You can even re-implement it with normal JavaScript code.

How would you "re-implement String.raw() with normal JavaScript code"? If I write my own template literal function const raw = (s, v) => ..., the string array s contains strings that are no longer raw. For example,

raw `a\nb`

will pass a-newline-b, whereas the raw string would be a-backslash-n-b.

My raw() template literal function could reverse the quoting by turning newline into backslash-n, but that's not reliable. How does it distinguish between:

raw `a\nb`

and

raw `a
b`

?

2

There are 2 answers

7
CherryDT On BEST ANSWER

The array that is passed as first argument to the template tag function has an additional property raw, as documented here. It contains the same as strings itself but as raw strings, without parsing escape sequences.

This means that String.raw could be replicated as follows:

function myRaw (strings, ...values) {
  return strings.raw.reduce((result, s, i) => result + s + (values[i] ?? ''), '')
}

Note that if we'd replace strings.raw.reduce with just strings.reduce, we'd get a "do-nothing" template tag function.

2
Jasmijn On

For this to work properly, you have to make use of the raw property of the first argument to the template function. It contains an array of the raw contents of the string as it exists in the source code, without backslash being treated as an escape character.

For example:

function raw(strings, ...values) {
  const output = [];
 
  for (let i = 0; i < strings.length; i++) {
    output.push(strings.raw[i]);  // <== instead of strings[i]
    if (i < values.length) {
      output.push(values[i]);
    }
  }
  
  return output.join("");
}