Skip to content

Don't reduce intersections of mappings of the same object type - #64481

Open
Anders Hejlsberg (ahejlsberg) wants to merge 3 commits into
mainfrom
fix-64415
Open

Anders Hejlsberg (ahejlsberg) wants to merge 3 commits into
mainfrom
fix-64415

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

With this PR, when a mapped type is applied to an intersection of homomorphic mappings of the same object type, we longer attempt to reduce the intersection. This in turn means that we don't eagerly resolve the members of the target object type, which helps enable creation of recursive schemas in Zod and similar libraries.

Fixes #64415.

Copilot AI balanced review requested due to automatic review settings September 27, 2026 15:54
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 27, 2026
@typescript-automation typescript-automation Bot added Author: Team For Backlog Bug PRs that fix a backlog bug labels Sep 27, 2026
@ahejlsberg

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) test it

@typescript-automation

typescript-automation Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

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

🟡 Changes recommended

The broad exemption prevents valid conflicting mapped intersections from reducing to never.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Prevents eager intersection reduction during recursive Zod-style schema inference.

Changes:

  • Detects mapped intersections sharing an object source.
  • Adds regression coverage and updates inferred-type baselines.
File Description
tsc/​internal/​checker/​checker.go Skips reduction for same-source mapped intersections.
tsc/​testdata/​tests/​cases/​compiler/​recursiveTypeInference2.ts Adds the Zod regression case.
tsc/​testdata/​baselines/​reference/​compiler/​recursiveTypeInference2.types Records inferred types.
tsc/​testdata/​baselines/​reference/​compiler/​recursiveTypeInference2.symbols Records resolved symbols.
tsc/​testdata/​baselines/​reference/​compiler/​recursiveTypeInference.types Updates existing recursive inference results.

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

if t.objectFlags&ObjectFlagsIsNeverIntersectionComputed == 0 {
t.objectFlags |= ObjectFlagsIsNeverIntersectionComputed
if core.Some(c.getPropertiesOfUnionOrIntersectionType(t), c.isNeverReducedProperty) {
if !c.isMappingOfSameObjectType(t.Types()) && core.Some(c.getPropertiesOfUnionOrIntersectionType(t), c.isNeverReducedProperty) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

True, but the goal here is to implement a heuristic that defers intersection reduction in cases where it is highly unlikely that impossible intersections will occur. An intersection consisting solely of homomorphic mapped types applied to the same object type is typically a conditional mapping that applies different templates to non-overlapping subsets of properties, and intersection reduction doesn't affect such types--except it forces eager member resolution for the constituent object type. Which is what we want to avoid.

@typescript-automation

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - native
Errors 41 41 ~ ~ ~ p=1.000 n=12
Symbols 115,480 115,480 ~ ~ ~ p=1.000 n=12
Types 96,949 96,949 ~ ~ ~ p=1.000 n=12
Memory Used 148,754k (± 0.29%) 148,218k (± 0.37%) ~ 146,933k 150,121k p=0.052 n=12
Memory Allocs 2,271,759 (± 0.00%) 2,271,626 (± 0.00%) -134 (- 0.01%) 2,271,421 2,271,882 p=0.028 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.041s (± 3.14%) 0.041s (± 4.25%) ~ 0.037s 0.047s p=0.745 n=12
Bind Time 0.012s (±12.99%) 0.012s (± 7.91%) ~ 0.010s 0.015s p=0.480 n=12
Check Time 0.501s (± 0.82%) 0.500s (± 1.03%) ~ 0.491s 0.517s p=0.560 n=12
Emit Time 0.270s (± 1.46%) 0.270s (± 2.23%) ~ 0.245s 0.279s p=0.599 n=12
Total Time 0.830s (± 0.67%) 0.829s (± 0.92%) ~ 0.799s 0.846s p=0.766 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 847,045 (± 0.08%) 846,835 (± 0.11%) ~ 843,932 849,017 p=0.920 n=12
Types 250,003 (± 0.00%) 250,004 (± 0.00%) ~ 250,001 250,014 p=0.917 n=12
Memory Used 793,187k (± 0.03%) 793,831k (± 0.08%) +644k (+ 0.08%) 792,084k 795,801k p=0.028 n=12
Memory Allocs 12,960,004 (± 0.04%) 12,957,291 (± 0.02%) ~ 12,954,238 12,966,176 p=0.443 n=12
Config Time 0.016s 0.016s ~ ~ ~ p=1.000 n=12
Parse Time 0.280s (± 1.71%) 0.275s (± 2.53%) ~ 0.250s 0.291s p=0.182 n=12
Bind Time 0.058s (± 0.86%) 0.062s (± 8.94%) ~ 0.058s 0.088s p=0.665 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.599s (± 0.46%) 1.619s (± 1.09%) +0.020s (+ 1.24%) 1.578s 1.687s p=0.029 n=12
Total Time 1.967s (± 0.35%) 1.987s (± 1.05%) ~ 1.950s 2.069s p=0.091 n=12
mui-docs - native
Errors 11,400 (± 0.04%) 11,400 (± 0.05%) ~ 11,376 11,408 p=0.763 n=12
Symbols 4,603,369 4,601,618 -1,751 (- 0.04%) ~ ~ p=0.000 n=12
Types 1,643,150 1,415,629 🟩-227,521 (-13.85%) ~ ~ p=0.000 n=12
Memory Used 4,956,058k (± 0.04%) 4,877,999k (± 0.03%) -78,059k (- 1.58%) 4,873,417k 4,880,612k p=0.000 n=12
Memory Allocs 48,662,811 (± 0.04%) 38,569,188 (± 0.04%) 🟩-10,093,624 (-20.74%) 38,525,522 38,614,810 p=0.000 n=12
Config Time 0.016s (± 1.55%) 0.016s (± 2.12%) ~ 0.015s 0.016s p=0.193 n=12
Parse Time 0.573s (± 2.20%) 0.585s (± 1.42%) ~ 0.563s 0.605s p=0.164 n=12
Bind Time 0.002s 0.002s ~ ~ ~ p=1.000 n=12
Check Time 15.152s (± 0.81%) 14.931s (± 0.53%) -0.221s (- 1.46%) 14.738s 15.061s p=0.007 n=12
Emit Time 0.440s (± 2.21%) 0.436s (± 0.33%) ~ 0.432s 0.440s p=0.348 n=12
Total Time 16.968s (± 0.71%) 16.740s (± 0.56%) -0.229s (- 1.35%) 16.524s 16.866s p=0.004 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,394,252 1,394,252 ~ ~ ~ p=1.000 n=12
Types 443,566 443,566 ~ ~ ~ p=1.000 n=12
Memory Used 1,711,016k (± 0.79%) 1,698,151k (± 1.04%) ~ 1,662,222k 1,747,684k p=0.347 n=12
Memory Allocs 93,674,821 (± 0.07%) 93,539,875 (± 0.15%) ~ 93,115,707 93,839,799 p=0.219 n=12
Config Time 0.004s (±10.30%) 0.004s (±17.94%) ~ 0.003s 0.007s p=0.718 n=12
Parse Time 0.214s (± 3.41%) 0.213s (± 4.13%) ~ 0.190s 0.232s p=0.965 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.830s (± 0.40%) 1.835s (± 0.78%) ~ 1.796s 1.882s p=0.660 n=12
Emit Time 0.291s (± 5.84%) 0.293s (± 4.99%) ~ 0.256s 0.329s p=0.832 n=12
Total Time 23.727s (± 0.56%) 23.697s (± 0.64%) ~ 23.392s 24.103s p=0.630 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 337,531 337,531 ~ ~ ~ p=1.000 n=12
Types 198,885 198,885 ~ ~ ~ p=1.000 n=12
Memory Used 319,776k (± 0.07%) 319,687k (± 0.02%) ~ 319,521k 319,855k p=0.932 n=12
Memory Allocs 4,673,215 (± 0.01%) 4,673,156 (± 0.01%) ~ 4,672,047 4,674,125 p=0.811 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.110s (± 4.71%) 0.112s (± 4.86%) ~ 0.098s 0.127s p=0.619 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.068s (± 0.42%) 1.067s (± 0.41%) ~ 1.058s 1.079s p=0.701 n=12
Emit Time 0.146s (± 6.30%) 0.136s (±11.14%) ~ 0.093s 0.157s p=0.259 n=12
Total Time 1.371s (± 0.56%) 1.365s (± 1.21%) ~ 1.325s 1.404s p=0.702 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 305,179 305,179 ~ ~ ~ p=1.000 n=12
Types 181,926 181,926 ~ ~ ~ p=1.000 n=12
Memory Used 277,160k (± 0.01%) 277,163k (± 0.03%) ~ 276,949k 277,302k p=0.701 n=12
Memory Allocs 1,642,312 (± 0.01%) 1,642,114 (± 0.01%) ~ 1,641,502 1,642,531 p=0.178 n=12
Config Time 0.000s (±217.90%) 0.000s (±217.90%) ~ 0.000s 0.001s p=1.000 n=12
Parse Time 0.102s (± 3.46%) 0.099s (± 3.48%) ~ 0.088s 0.106s p=0.224 n=12
Bind Time 0.036s (±11.37%) 0.039s (±10.35%) ~ 0.027s 0.049s p=0.283 n=12
Check Time 0.837s (± 0.56%) 0.838s (± 0.65%) ~ 0.825s 0.851s p=0.831 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.989s (± 0.88%) 0.989s (± 0.81%) ~ 0.966s 1.007s p=0.943 n=12
vscode - native
Errors 371 371 ~ ~ ~ p=1.000 n=12
Symbols 9,894,472 9,889,352 -5,120 (- 0.05%) ~ ~ p=0.000 n=12
Types 3,335,673 3,334,351 -1,322 (- 0.04%) ~ ~ p=0.000 n=12
Memory Used 6,500,296k (± 0.02%) 6,498,464k (± 0.01%) -1,833k (- 0.03%) 6,496,309k 6,501,517k p=0.010 n=12
Memory Allocs 49,995,466 (± 0.01%) 49,995,323 (± 0.03%) ~ 49,975,509 50,060,230 p=0.160 n=12
Config Time 0.066s (± 0.27%) 0.066s (± 0.41%) ~ 0.065s 0.067s p=1.000 n=12
Parse Time 1.795s (± 2.53%) 1.854s (± 3.00%) ~ 1.729s 1.967s p=0.124 n=12
Bind Time 0.517s (±17.62%) 0.466s (±16.44%) ~ 0.403s 0.802s p=0.876 n=12
Check Time 13.477s (± 1.07%) 13.515s (± 1.12%) ~ 13.053s 13.775s p=0.478 n=12
Emit Time 4.152s (±11.86%) 4.230s (±12.71%) ~ 3.703s 5.683s p=0.128 n=12
Total Time 20.117s (± 1.96%) 20.241s (± 2.15%) ~ 19.691s 21.418s p=0.284 n=12
webpack - native
Errors 848 848 ~ ~ ~ p=1.000 n=12
Symbols 1,447,477 1,440,949 -6,528 (- 0.45%) ~ ~ p=0.000 n=12
Types 618,246 618,246 ~ ~ ~ p=1.000 n=12
Memory Used 996,583k (± 0.02%) 994,411k (± 0.02%) -2,171k (- 0.22%) 993,991k 994,727k p=0.000 n=12
Memory Allocs 6,364,068 (± 0.02%) 6,340,957 (± 0.04%) -23,111 (- 0.36%) 6,337,797 6,351,721 p=0.000 n=12
Config Time 0.009s 0.009s ~ ~ ~ p=1.000 n=12
Parse Time 0.262s (± 2.92%) 0.263s (± 3.18%) ~ 0.244s 0.288s p=0.810 n=12
Bind Time 0.075s (±19.09%) 0.076s (±15.26%) ~ 0.055s 0.111s p=0.943 n=12
Check Time 1.998s (± 0.48%) 1.986s (± 0.45%) ~ 1.970s 2.019s p=0.091 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 2.381s (± 0.44%) 2.373s (± 0.43%) ~ 2.354s 2.415s p=0.202 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,045,695 1,045,648 -47 (- 0.00%) ~ ~ p=0.000 n=12
Types 383,049 383,035 -14 (- 0.00%) ~ ~ p=0.000 n=12
Memory Used 611,355k (± 0.01%) 611,251k (± 0.02%) ~ 611,047k 611,478k p=0.101 n=12
Memory Allocs 4,830,373 (± 0.06%) 4,829,547 (± 0.05%) ~ 4,823,863 4,837,108 p=0.671 n=12
Config Time 0.003s (±12.54%) 0.003s (±10.34%) ~ 0.002s 0.003s p=0.667 n=12
Parse Time 0.145s (± 3.13%) 0.154s (± 4.74%) ~ 0.141s 0.179s p=0.155 n=12
Bind Time 0.045s (±21.22%) 0.043s (±19.58%) ~ 0.033s 0.075s p=0.595 n=12
Check Time 1.190s (± 0.60%) 1.230s (± 3.28%) ~ 1.176s 1.347s p=0.117 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.392s (± 0.49%) 1.440s (± 3.48%) ~ 1.367s 1.585s p=0.066 n=12
System info unknown
Hosts
  • native
Scenarios
  • Compiler-Unions - native
  • angular-1 - native
  • mui-docs - native
  • strada-build-src - native
  • strada-compiler - native
  • ts-pre-modules - native
  • vscode - native
  • webpack - native
  • xstate-main - native
Benchmark Name Iterations
Current pr 12
Baseline baseline 12

Developer Information:

Download Benchmarks

@typescript-automation

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing main and refs/pull/64481/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Git clone failed"
  • 1 instance of "Package install failed"

Otherwise...

Everything looks good!

@typescript-automation

Copy link
Copy Markdown

Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@ahejlsberg

Copy link
Copy Markdown
Member Author

Apparently this pattern accounts for a substantial number of types in mui-docs, so nice savings there.

@typescript-automation

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/64481/merge:

Everything looks good!

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

Author: Team For Backlog Bug PRs that fix a backlog bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

Constraint check resolves an un-annotated accessor while its object literal is still being inferred

2 participants