Skip to content

Erasing a const enum can emit an illegal "use strict" directive #64049

Description

@magic-akari

🔎 Search Terms

const enum, preserveConstEnums, use strict, directive prologue, non-simple parameter list, type erasure, invalid JavaScript emit

🕗 Version & Regression Information

  • Reproduced with TypeScript 6.0.3.
  • I could not find an existing issue for this specific interaction after searching for the terms above.

⏯ Playground Link

TypeScript Playground

💻 Code

function foo(a = 1) {
    const enum E { x }
    "use strict"
}

🙁 Actual behavior

With the default preserveConstEnums: false, TypeScript accepts the source and erases the const enum. The relevant emitted function is:

function foo(a = 1) {
    "use strict";
}

The string is now the first statement in the function body and is reinterpreted as a Use Strict Directive. The emitted JavaScript is syntactically invalid because the function has a non-simple parameter list:

SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list

TypeScript does not report TS1347 on the source because the preceding enum declaration prevents the string from being recognized as part of the source directive prologue.

With preserveConstEnums: true, TypeScript emits the enum's runtime representation before the string. The string therefore remains an ordinary expression statement and the output is valid. The same accepted source consequently produces either valid or invalid JavaScript depending on whether the const enum is preserved.

🙂 Expected behavior

TypeScript should not emit syntactically invalid JavaScript from this accepted source. Erasing the const enum must preserve the boundary that keeps the following string expression from becoming a Use Strict Directive.

The exact emitted syntax and quote style are not important. For example, TypeScript could parenthesize the expression:

function foo(a = 1) {
    ("use strict");
}

Alternatively, it could emit an empty statement at the position of the erased declaration, preserving the directive-prologue boundary:

function foo(a = 1) {
    ;
    "use strict";
}

Either output keeps the string as an ordinary expression statement and produces valid JavaScript.

Additional information about the issue

This is related to, but distinct from, #64033.

That issue discusses declarations such as type aliases that are always removed from JavaScript output. The TypeScript team response proposes treating those type-only declarations as transparent when determining the directive prologue.

A const enum is different because it is not unconditionally absent from the output: preserveConstEnums determines whether TypeScript erases it or emits a runtime enum. When it is preserved, it clearly precedes the string at runtime and prevents the string from being a directive. Erasing it should not silently change that source-level classification and, in this example, turn accepted TypeScript into JavaScript that cannot be parsed.

Even if always-erased type-only declarations are treated as transparent for #64033, the option-dependent behavior of const enum requires separate consideration.

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