Repository navigation
Dedupe identical object literal types before subtype reduction - #64729
Open
Camille Barneaud (gadcam) wants to merge 2 commits into
Open
Camille Barneaud (gadcam) wants to merge 2 commits into
Camille Barneaud (gadcam) wants to merge 2 commits into
Conversation
removeSubtypes compares every pair of constituents, which makes array literals with many records quadratic. Large JSON imports hit this hard: the colornerd.json file from microsoft#48364 (29K objects) never finishes, using gigabytes for the relation cache. Object literal types whose properties have the same names, flags and already-resolved types are identical, so the pairwise loop would keep only the first of them anyway. Remove them up front, keyed by a hash of their shape. Property types are only read when already resolved, so the new check never triggers type resolution. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise. |
Copilot started reviewing on behalf of
Camille Barneaud (gadcam)
October 10, 2026 18:02
View session
Author
|
TypeScript Bot (@typescript-bot) It fixes issue #48364 |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Shape-key computation can trigger member resolution and the test does not detect regression of the optimization.
2 open findings
What changed in this PR
Optimizes subtype reduction by deduplicating structurally identical object literal types before pairwise comparison.
Changes:
- Adds object-literal shape hashing and deduplication.
- Adds compiler coverage and generated baselines for JSON and object-literal cases.
| File | Description |
|---|---|
tsc/internal/checker/checker.go |
Implements shape-based deduplication. |
tsc/testdata/tests/cases/compiler/arrayLiteralIdenticalObjectLiteralsSubtypeReduction.ts |
Adds compiler scenarios. |
tsc/testdata/baselines/reference/compiler/arrayLiteralIdenticalObjectLiteralsSubtypeReduction.types |
Records inferred types. |
tsc/testdata/baselines/reference/compiler/arrayLiteralIdenticalObjectLiteralsSubtypeReduction.symbols |
Records resolved symbols. |
tsc/testdata/baselines/reference/compiler/arrayLiteralIdenticalObjectLiteralsSubtypeReduction.js |
Records emit and declarations. |
🧠 Review effort: Balanced
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot started reviewing on behalf of
Camille Barneaud (gadcam)
October 10, 2026 18:41
View session
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes #48364: this fixes the checker blow-up on large JSON arrays. Editor responsiveness on very large JSON files may still warrant
.d.json.ts.removeSubtypesrelates every pair of union constituents. Array literals of records, typically fromresolveJsonModuleimports, produce one object literal type per element, so their element type costs O(n²) structural checks, and the relation cache grows with them.This removes object literal types that are identical to an earlier constituent before the pairwise loop. Identical types are mutual subtypes, so the loop would keep only the first anyway and the reduced type is the same. One visible difference: array literals with many records of one shape followed by another shape (e.g. 1,000 ×
{ a: "" }+ 100 ×{ a: 0 }) no longer hit the subtype reduction limit and report a false TS2590. Two types count as identical when they have the same hash of their object flags and properties (names, flags, check flags and types), like the otherCacheHashKey-keyed caches in the checker. Members and property types are only read when they are already resolved (valueSymbolLinks.resolvedType, no accessors, methods, or deferred/instantiated/mapped properties), so the check never triggers type resolution and can't change circularity behavior (see #46981).Results
colornerd.json from #48364 (29,177 objects, 3 shapes):
The blow-up starts between 23K and 24K elements. Below that, the same file checks in ~0.1s.
Interleaved A/B runs (Mann-Whitney U on wall time):
Type and symbol counts are identical in every run. Declaration emit for the JSON imports is identical.
Tests
arrayLiteralIdenticalObjectLiteralsSubtypeReduction.tscovers JSON records with reordered keys, mixed property types andnull, plus the cases the shape key must handle or skip: reordered keys, optional properties, spreads, methods, accessors andas const. Its baselines are byte-identical with and without the checker change, and a temporary trace confirmed the new path runs for most of these cases.arrayLiteralManyObjectLiteralsNotTooComplex.tsreports TS2590 without this change and no error with it.AI disclosure
This change was written with Claude Code (investigation, patch, test, and benchmarks). I have reviewed it, understand it, and will handle review feedback myself.