Skip to content

Merge export * tables without per-name collision trackers - #64722

Draft
Gavin Kline (gwkline) wants to merge 1 commit into
microsoft:mainfrom
gwkline:perf/export-star-tables
Draft

Gavin Kline (gwkline) wants to merge 1 commit into
microsoft:mainfrom
gwkline:perf/export-star-tables

Conversation

@gwkline

@gwkline Gavin Kline (gwkline) commented Oct 10, 2026 •

Copy link
Copy Markdown

Fixes #64719

Analysis

See #64719. For each module with export *, getExportsOfModuleWorker merged every re-exported table into a scratch table, allocating an ExportCollision record (with the module specifier text) for every distinct re-exported name so it could report TS2308 later. It then walked that table again to find collisions and merged the scratch table into the result. It also copied every visited module's export names into a set that only mattered when an export type * was present.

So every export * layer cost several allocations per re-exported name, even with a single export *, where nothing can collide. Barrels over large modules (generated clients, or date-fns- and AWS-SDK-style entry points) pay this at every layer. The generator in the issue spends most of its check time here.

Fix

  • Re-exported tables are merged directly into the module's result table, which is pre-sized for the module's own exports plus its largest re-exported table. Without the pre-sizing, a 20k-name table grows through a dozen rehashes at every barrel layer, which is most of the remaining time.
  • Collision bookkeeping only exists when a module has more than one export *: a map from each re-exported name to the declaration that first contributed it. A later declaration that contributes a different symbol under that name is reported against the first one as it is merged. The module's own exports are in the table before any export * and never in that map, so re-exporting one of them is still not an error.
  • Instead of a set of names, the worker records the modules reached through a non-type-only path, and applies them to typeOnlyExportStarMap only when that map exists.
  • ExportCollision, ExportCollisionTable and extendExportSymbols had no other users and are removed.

Semantics

Unchanged. The first contributing export * still wins. A module's own exports still shadow re-exports without an error. default is skipped, export= is never reported as TS2308, and the export type * / export * override rules are the same. Diagnostics used to be emitted in map order and are now emitted in declaration order; they are sorted before output either way.

New test exportStarNameCollisions covers:

  • collisions from two and three declarations
  • the same symbol reached twice
  • a local export silencing a collision
  • a single export *
  • export type * on either side
  • a chain of barrels

Its baselines were generated with the old code and are unchanged. No existing baseline changes.

Results

M3 Pro, medians of alternating runs:

Program Check time Allocations
generator from the issue (207 source files) 0.266 s → 0.058 s 1.58M → 0.24M
private monorepo app (14.5k files) 8.40 s → 8.12 s 69.5M → 68.4M

The monorepo number is a 3% median improvement over eight runs, but run-to-run noise on that program is about ±10%, so treat it as "no regression" rather than a measured win. The generator result is stable across runs.

The profiling, the patch and this description were produced with Claude Code (Claude Fable 5.1 and Claude Opus 5.5). I have read and understand the change and will handle review myself.

Copilot Checklist

I successfully ran the applicable command at the end of my session, and it completed without error:

  • npx hereby validate
  • npx hereby validate --api (for TypeScript API changes; no API changes here)

🤖 Generated with Claude Code

@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 10, 2026
@jakebailey
Jake Bailey (jakebailey) requested a balanced review from Copilot October 10, 2026 12:11

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.

🔵 Needs a closer look

The optimization changes core recursive module-export resolution and warrants final human validation despite strong regression coverage.

0 open findings

What changed in this PR

Optimizes compiler resolution of export * tables while preserving collision and type-only export semantics.

Changes:

  • Merges re-export tables directly with preallocated capacity.
  • Defers collision tracking and diagnostic text creation.
  • Adds comprehensive compiler regression coverage and baselines.
File Description
tsc/​internal/​checker/​checker.go Optimizes export-table merging and collision tracking.
tsc/​testdata/​tests/​cases/​compiler/​exportStarNameCollisions.ts Adds export-star collision scenarios.
tsc/​testdata/​baselines/​reference/​compiler/​exportStarNameCollisions.errors.txt Captures expected diagnostics.
tsc/​testdata/​baselines/​reference/​compiler/​exportStarNameCollisions.js Captures emit and declarations.
tsc/​testdata/​baselines/​reference/​compiler/​exportStarNameCollisions.symbols Captures resolved symbols.
tsc/​testdata/​baselines/​reference/​compiler/​exportStarNameCollisions.types Captures inferred types.

🧠 Review effort: Balanced


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

@gwkline

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@typescript-automation typescript-automation Bot added For Backlog Bug PRs that fix a backlog bug and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 21, 2026

This branch has not been deployed

No deployments
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

None yet

Development

Successfully merging this pull request may close these issues.

export * resolution allocates per re-exported name, slowing checks of barrels over large modules

2 participants