Skip to content

[7.0] Downlevel emit places a comment after a synthesized return, causing arrow functions to return undefinedΒ #63746

Description

πŸ”Ž Search Terms

"arrow function", "optional chaining", "comment", "return undefined", "ASI", "downlevel emit"

πŸ•— Version & Regression Information

  • This changed between versions 6.0.3 and 7.0.2

⏯ Playground Link

No response

πŸ’» Code

const before: { color?: { text: string } } = {
  color: { text: "black" },
};

const after: { color?: { text: string } } = {
  color: { text: "white" },
};

const changed = () =>
  // Compare the colors.
  before.color?.text !== after.color?.text;

console.log(changed());

Compiler options

{
  "compilerOptions": {
    "target": "es2019",
    "module": "commonjs"
  }
}

πŸ™ Actual behavior

TypeScript 7.0.2 emits:

const changed = () => {
    var _a, _b;
    return
    // Compare the colors.
    ((_a = before.color) === null || _a === void 0
        ? void 0
        : _a.text) !==
        ((_b = after.color) === null || _b === void 0
            ? void 0
            : _b.text);
};

Due to JavaScript automatic semicolon insertion, the generated function is equivalent to:

const changed = () => {
    return;
};

Runtime output:
undefined
The comparison is never evaluated.

πŸ™‚ Expected behavior

The generated return statement should contain the transformed expression.
TypeScript 6.0.3 emits code equivalent to:

const changed = () => {
    var _a, _b;
    // Compare the colors.
    return ((_a = before.color) === null || _a === void 0
        ? void 0
        : _a.text) !==
        ((_b = after.color) === null || _b === void 0
            ? void 0
            : _b.text);
};

Runtime output:
true

Additional information about the issue

Reproduction commands
TypeScript 7:

npx -p typescript@7.0.2 tsc repro.ts \
  --ignoreConfig \
  --target es2019 \
  --module commonjs
node repro.js
Output:
undefined
TypeScript 6:
npx -p typescript@6.0.3 tsc repro.ts \
  --ignoreConfig \
  --target es2019 \
  --module commonjs

node repro.js

Output:
true

The issue appears when all of the following are present:

  • An arrow function uses a concise expression body.
  • A comment appears immediately after =>.
  • The expression requires downlevel transformation, such as optional chaining when targeting ES2019.
  • The emitter converts the arrow body into a block and synthesizes a return.
  • The comment is emitted after the synthesized return, separated by a newline, which changes the runtime semantics due to automatic semicolon insertion.

Known workarounds:

  • Use an explicit block and return (...).
  • Move the comment before the arrow function declaration.
  • Remove the comment.
  • Target ES2020 or newer so optional chaining is not downleveled.
  • Compile with TypeScript 6.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions