Typescript: Template strings are cached incorrectly

Created on 30 Sep 2018  路  2Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.2.0-dev.20180929

Search Terms:

  • template literal

Code

var invokes = [];
function test(template, _) {
  invokes.push(template); 
}

function update(value) {
  test`some ${value}!`;
}

update(1);
update(2);

document.body.textContent = '' + (invokes[0] === invokes[1]); 

Expected behavior:
After compiling and running the code in a browser console, the body's content should be "true".

The code works as expected when compiled with Babel, or run verbatim in a browser console.

Actual behavior:
The body's content becomes "false". This breaks libraries such as hyperHTML (note: now it contains a workaround that works for me at least https://github.com/WebReflection/hyperHTML/commit/6d3d87954625ffec4a7cc9e5fef49c99388060e9)

https://github.com/WebReflection/hyperHTML/issues/270

Playground Link: https://www.typescriptlang.org/play/#src=var%20invokes%20%3D%20%5B%5D%3B%0D%0Afunction%20test(template%2C%20_)%20%7B%0D%0A%20%20invokes.push(template)%3B%20%0D%0A%7D%0D%0A%0D%0Afunction%20update(value)%20%7B%0D%0A%20%20test%60some%20%24%7Bvalue%7D!%60%3B%0D%0A%7D%0D%0A%0D%0Aupdate(1)%3B%0D%0Aupdate(2)%3B%0D%0A%0D%0Adocument.body.textContent%20%3D%20''%20%2B%20(invokes%5B0%5D%20%3D%3D%3D%20invokes%5B1%5D)%3B%20%0D%0A

Related Issues:

Bug ES6

Most helpful comment

Close to 2 years, and still no fixes, but people keep filing issues to my libraries:
https://github.com/ungap/template-literal/issues/10

My workaround is also mostly reliable, but it's based on "_signing_" each template literal, assuming two templates literals with the same content comes from the same scope/callback.

This plays well until you have people doing, for a reason or another, the following:

const componentA = content => html`${content}`;
const componentB = content => html`${content}`;

render(where, html`${componentA()}${componentB()}`);

All three empty template literals produces the same template signature, so there's no way to reasonably cache and assign a unique identifier to each of them.

A better playground has been created:
https://bit.ly/2XYvAmY

it's based on the following code:

function tag(template, ...args) {
  set.add(template);
  const out = [template[0]];
  for (let i = 1, { length } = template; i < length; i++)
    out.push(args[i - 1], template[i]);
  return out.join('');
}

const set = new Set;

const a = () => tag`${1}`;
const b = () => tag`${2}`;

console.assert(
  a() === a() && b() === b(),
  'transpilation error'
);

console.assert(
  set.size === 2,
  `expected 2 unique templates, got ${set.size} instead`
);

This issue is easily breaking in the wild otherwise perfectly valid code, and as there's no workaround to solve this in user-land, except telling people to not use TypeScript to transpile, I hope it'll get a higher priority.

The current measure I'm considering in here, is to remove any normalization, which will penalize TypeScript users performances as there will be tons of unnecessary DOM trashes in code transpiled in the wild, including CodePens, code in any html page, etc etc.

Best Regards.

All 2 comments

The problem here is that we do the right thing in modules, but not in the global scope because we don't want to have potentially conflicting variables. So we could just come up with absurdly long variable names that describe the string contents and work with that.

Close to 2 years, and still no fixes, but people keep filing issues to my libraries:
https://github.com/ungap/template-literal/issues/10

My workaround is also mostly reliable, but it's based on "_signing_" each template literal, assuming two templates literals with the same content comes from the same scope/callback.

This plays well until you have people doing, for a reason or another, the following:

const componentA = content => html`${content}`;
const componentB = content => html`${content}`;

render(where, html`${componentA()}${componentB()}`);

All three empty template literals produces the same template signature, so there's no way to reasonably cache and assign a unique identifier to each of them.

A better playground has been created:
https://bit.ly/2XYvAmY

it's based on the following code:

function tag(template, ...args) {
  set.add(template);
  const out = [template[0]];
  for (let i = 1, { length } = template; i < length; i++)
    out.push(args[i - 1], template[i]);
  return out.join('');
}

const set = new Set;

const a = () => tag`${1}`;
const b = () => tag`${2}`;

console.assert(
  a() === a() && b() === b(),
  'transpilation error'
);

console.assert(
  set.size === 2,
  `expected 2 unique templates, got ${set.size} instead`
);

This issue is easily breaking in the wild otherwise perfectly valid code, and as there's no workaround to solve this in user-land, except telling people to not use TypeScript to transpile, I hope it'll get a higher priority.

The current measure I'm considering in here, is to remove any normalization, which will penalize TypeScript users performances as there will be tons of unnecessary DOM trashes in code transpiled in the wild, including CodePens, code in any html page, etc etc.

Best Regards.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zhuravlikjb picture zhuravlikjb  路  3Comments

Antony-Jones picture Antony-Jones  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments

weswigham picture weswigham  路  3Comments

wmaurer picture wmaurer  路  3Comments