Skip to content

New Template Literal Flags are confused for ContainsInvalidEscape causing tagged template expressions to be unnecessarily transpiled #55369

@engelsdamien

Description

@engelsdamien

🔎 Search Terms

In processTaggedTemplateExpression the code checks whether the tagged template expression using the hasInvalidEscape function. This was done in #12700 to support a new feature in tagged template literals allowing for invalid escape sequences.

Unfortunately, the function was written in a way that relies on implicit from number -> boolean instead of masking the templateFlags field:

/** @internal */
export function hasInvalidEscape(template: TemplateLiteral): boolean {
    return template && !!(isNoSubstitutionTemplateLiteral(template)
        ? template.templateFlags
        : (template.head.templateFlags || some(template.templateSpans, span => !!span.literal.templateFlags)));
}

This means the code assumes any template literal with non-zero flags contains an invalid escape. While this was true in the past, new flags were introduced in 5.1 breaking the logic of the code.

Now all tagged templates containing unicode escape sequences now get transpiled when they don't need to, causing a code size increase.

🕗 Version & Regression Information

This changed between versions 5.0.4 and 5.1.6

v5.0.4: https://www.typescriptlang.org/play?target=3&jsx=0&module=1&ts=5.0.4#code/GYVwdgxgLglg9mABMOcA8AVAfACgIYBciGAlAN4BOAplCBUngNwC+AUBAgM5SJ6IC8yVAAMAOiAAMUicMbsuPAEYChcYXllA

v5.1.6: https://www.typescriptlang.org/play?target=3&jsx=0&module=1&ts=5.1.6#code/GYVwdgxgLglg9mABMOcA8AVAfACgIYBciGAlAN4BOAplCBUngNwC+AUBAgM5SJ6IC8yVAAMAOiAAMUicMbsuPAEYChcYXllA

⏯ Playground Link

https://www.typescriptlang.org/play?target=3&jsx=0&module=1&ts=5.1.6#code/GYVwdgxgLglg9mABMOcA8AVAfACgIYBciGAlAN4BOAplCBUngNwC+AUBAgM5SJ6IC8yVAAMAOiAAMUicMbsuPAEYChcYXllA

💻 Code

function foo<T>(a: T){return a;}
const a = foo`\u0000`;
const b = foo`a`;

🙁 Actual behavior

"use strict";
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
    if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
    return cooked;
};
function foo(a) { return a; }
const a = foo(__makeTemplateObject(["\0"], ["\\u0000"]));
const b = foo `a`;

🙂 Expected behavior

"use strict";
function foo(a) { return a; }
const a = foo `\u0000`;
const b = foo `a`;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions