Skip to content

Elide empty named imports next to a default import under verbatimModuleSyntax - #64578

Merged
Ryan Cavanaugh (RyanCavanaugh) merged 3 commits into
microsoft:mainfrom
maksim-romanov:fix-verbatim-empty-named-imports
Oct 2, 2026
Merged

Ryan Cavanaugh (RyanCavanaugh) merged 3 commits into
microsoft:mainfrom
maksim-romanov:fix-verbatim-empty-named-imports

Conversation

@maksim-romanov

@maksim-romanov Maksim Romanov (maksim-romanov) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #62239

With verbatimModuleSyntax on, tsc kept an empty {} after a default import when all the named imports were type-only:

import A, { type T } from "./a";
// before: import A, {} from "./a";
// after:  import A from "./a";

The default binding keeps the import, so the braces do nothing there. Imports without a default binding still emit import {} from "./a", and a {} you write in the source stays. 6.0 emits the braces too, and this PR changes that. Ryan approved the same change for the old codebase in #62349 before the bot closed it during the 6.0 freeze.

I wrote this change with Claude Code. I chose this issue, read and understand the change, and will handle review feedback myself.

…leSyntax

With verbatimModuleSyntax, erasing every type-only specifier next to a default import used to leave behind an empty {}. The default binding already keeps the import, so the empty braces are dropped now. Braces written in the source and imports without a default binding are unchanged.

Fixes microsoft#62239
Copilot AI balanced review requested due to automatic review settings October 1, 2026 20:56
@typescript-automation typescript-automation Bot added the For Backlog Bug PRs that fix a backlog bug label Oct 1, 2026
@maksim-romanov

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The narrow guard matches the intended behavior, regression cases cover its boundaries, and no blocking issues were identified.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes #62239 by dropping named-import braces emptied by type erasure when a default import remains under verbatimModuleSyntax.

Changes:

  • Updates the type eraser while preserving source-written {} and imports without a default binding.
  • Adds regression coverage and matching emit, type, and symbol baselines.
File Description
tsc/​testdata/​tests/​cases/​compiler/​verbatimModuleSyntaxDefaultImportTypeOnlyNamed.ts Tests elision and preservation cases.
tsc/​testdata/​baselines/​reference/​compiler/​verbatimModuleSyntaxDefaultImportTypeOnlyNamed.types Records expected types.
tsc/​testdata/​baselines/​reference/​compiler/​verbatimModuleSyntaxDefaultImportTypeOnlyNamed.symbols Records expected symbols.
tsc/​testdata/​baselines/​reference/​compiler/​verbatimModuleSyntaxDefaultImportTypeOnlyNamed.js Records expected import emission.
tsc/​internal/​transformers/​tstransforms/​typeeraser.go Removes redundant empty named bindings.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@jakebailey

Copy link
Copy Markdown
Member

I thought this behavior was intentional? Andrew Branch (@andrewbranch)

@maksim-romanov

Maksim Romanov (maksim-romanov) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Ryan marked #62239 Help Wanted and approved the same fix in #62349. import { type T } still emits import {}, only the default import case changes.

@jakebailey

Copy link
Copy Markdown
Member

Yes, because import { type Foo } from "foo" means "erase Foo", which nets import {} from "foo". This is also how erasable types works in Node, 99% sure.

@maksim-romanov

Copy link
Copy Markdown
Contributor Author

Makes sense. Node's type stripping, ts-blank-space and esbuild all keep the {} too, so the current output is the consistent one. Closing.

@jakebailey

Copy link
Copy Markdown
Member

Sorry, I misread the issue; I didn't realize that the first default import was not also a type import. I think eliding the {} is fine so long as default is not a type import.

@jakebailey

Copy link
Copy Markdown
Member

import type A1, { type T } is illegal already, because we smartly realized this would be ambiguous 🤦

Comment thread tsc/internal/transformers/tstransforms/typeeraser.go
@RyanCavanaugh
Ryan Cavanaugh (RyanCavanaugh) added this pull request to the merge queue Oct 1, 2026
Merged via the queue into microsoft:main with commit 09b1db0 Oct 2, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Backlog Bug PRs that fix a backlog bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

verbatimModuleSyntax leaves empty curly brackets in the JS output

4 participants