Skip to content

[experiment] Skip incompatible discriminants when inferring from union sources - #64695

Open
Mateusz Burzyński (Andarist) wants to merge 1 commit into
microsoft:mainfrom
Andarist:fix-go/contextual-inference-discriminants
Open

Mateusz Burzyński (Andarist) wants to merge 1 commit into
microsoft:mainfrom
Andarist:fix-go/contextual-inference-discriminants

Conversation

@Andarist

Copy link
Copy Markdown
Contributor

fixes #64690

I was kinda always wondering if a logic like this could be used to cut down on inference work when dealing with discriminated unions and the referenced PR pushed me to try it out.

I'd still like to do more work on this and recheck things. That's why I'm opening this as a draft, I'd be curious what extended user tests etc would show for this

Copilot AI balanced review requested due to automatic review settings October 8, 2026 17:21
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 8, 2026
@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it

@typescript-automation

typescript-automation Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

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.

🔵 Needs a closer look

The draft changes broad compiler inference semantics despite strong regression coverage and warrants human evaluation against extended user tests.

0 open findings

What changed in this PR

Adds discriminant-aware filtering to union-source inference, addressing #64690 while expanding compiler regression coverage.

Changes:

  • Skips source constituents with incompatible fixed discriminants.
  • Tests aliases, nesting, tag types, ordering, and optional-property semantics.
  • Adds diagnostics, type, and symbol baselines for both optional-property modes.
File Description
tsc/​internal/​checker/​inference.go Implements discriminant-aware inference filtering.
tsc/​testdata/​tests/​cases/​compiler/​inferenceDiscriminatedUnionSource.ts Adds compiler regression cases.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=true).types Records inferred types.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=true).symbols Records symbols.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=true).errors.txt Records diagnostics.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=false).types Records inferred types.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=false).symbols Records symbols.
tsc/​testdata/​baselines/​reference/​compiler/​inferenceDiscriminatedUnionSource(exactoptionalpropertytypes=false).errors.txt Records diagnostics.

🧠 Review effort: Balanced


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

@typescript-automation

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-automation

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the user tests with tsc comparing baseline and pr:

Everything looks good!

@typescript-automation

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey)
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,276 115,344 +68 (+ 0.06%) ~ ~ p=0.000 n=12
Types 79,613 79,825 +212 (+ 0.27%) ~ ~ p=0.000 n=12
Memory Used 139,467k (± 0.05%) 139,524k (± 0.07%) ~ 139,283k 139,868k p=0.291 n=12
Memory Allocs 2,190,609 (± 0.00%) 2,191,450 (± 0.01%) +842 (+ 0.04%) 2,191,208 2,191,918 p=0.000 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.041s (± 4.59%) 0.042s (± 3.81%) ~ 0.037s 0.046s p=0.466 n=12
Bind Time 0.013s (±11.42%) 0.013s (±15.40%) ~ 0.010s 0.018s p=0.651 n=12
Check Time 0.496s (± 0.81%) 0.499s (± 0.88%) ~ 0.490s 0.512s p=0.324 n=12
Emit Time 0.271s (± 1.78%) 0.271s (± 1.74%) ~ 0.263s 0.291s p=0.743 n=12
Total Time 0.827s (± 0.53%) 0.831s (± 0.89%) ~ 0.815s 0.856s p=0.522 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 845,646 (± 0.07%) 846,217 (± 0.08%) ~ 844,344 847,855 p=0.266 n=12
Types 248,123 (± 0.00%) 251,330 (± 0.00%) +3,206 (+ 1.29%) 251,326 251,340 p=0.000 n=12
Memory Used 788,084k (± 0.03%) 789,200k (± 0.02%) +1,116k (+ 0.14%) 788,766k 789,559k p=0.000 n=12
Memory Allocs 12,920,468 (± 0.02%) 12,931,329 (± 0.03%) +10,861 (+ 0.08%) 12,924,217 12,946,521 p=0.000 n=12
Config Time 0.016s (± 1.97%) 0.016s (± 1.75%) ~ 0.016s 0.017s p=0.667 n=12
Parse Time 0.263s (± 1.85%) 0.253s (± 3.42%) ~ 0.225s 0.271s p=0.061 n=12
Bind Time 0.059s (± 2.22%) 0.069s (±17.04%) 🔻+0.011s (+18.07%) 0.059s 0.119s p=0.013 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.584s (± 0.76%) 1.597s (± 1.43%) ~ 1.553s 1.669s p=0.523 n=12
Total Time 1.935s (± 0.72%) 1.950s (± 1.46%) ~ 1.895s 2.026s p=0.580 n=12
mui-docs - native
Errors 11,405 11,405 ~ ~ ~ p=1.000 n=12
Symbols 4,359,614 4,359,688 +74 (+ 0.00%) ~ ~ p=0.000 n=12
Types 1,376,379 1,382,217 +5,838 (+ 0.42%) ~ ~ p=0.000 n=12
Memory Used 2,954,247k (± 0.06%) 2,955,020k (± 0.05%) ~ 2,951,708k 2,960,703k p=0.514 n=12
Memory Allocs 32,839,861 (± 0.07%) 32,841,660 (± 0.04%) ~ 32,819,481 32,866,395 p=0.977 n=12
Config Time 0.016s (± 1.97%) 0.017s (± 1.45%) ~ 0.016s 0.017s p=0.089 n=12
Parse Time 0.502s (± 1.89%) 0.492s (± 2.84%) ~ 0.452s 0.516s p=0.385 n=12
Bind Time 0.002s (±31.13%) 0.002s ~ ~ ~ p=1.000 n=12
Check Time 8.681s (± 0.51%) 8.637s (± 0.68%) ~ 8.528s 8.788s p=0.272 n=12
Emit Time 0.487s (±14.75%) 0.528s (±14.10%) ~ 0.419s 0.682s p=0.172 n=12
Total Time 10.449s (± 0.31%) 10.450s (± 0.52%) ~ 10.269s 10.566s p=0.767 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,390,229 1,394,216 +3,987 (+ 0.29%) ~ ~ p=0.000 n=12
Types 441,498 450,921 +9,423 (+ 2.13%) ~ ~ p=0.000 n=12
Memory Used 1,685,848k (± 0.99%) 1,679,834k (± 0.85%) ~ 1,661,307k 1,723,079k p=0.590 n=12
Memory Allocs 92,117,106 (± 0.15%) 92,360,812 (± 0.17%) +243,706 (+ 0.26%) 92,046,435 92,717,754 p=0.020 n=12
Config Time 0.004s (±10.74%) 0.003s (±18.62%) 🟩-0.001s (-19.15%) 0.002s 0.005s p=0.048 n=12
Parse Time 0.199s (± 4.36%) 0.193s (± 3.28%) ~ 0.179s 0.214s p=0.173 n=12
Bind Time 0.000s (±217.90%) 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.813s (± 0.66%) 1.820s (± 0.54%) ~ 1.801s 1.849s p=0.291 n=12
Emit Time 0.291s (± 6.21%) 0.303s (± 2.82%) ~ 0.280s 0.327s p=0.417 n=12
Total Time 23.517s (± 0.73%) 23.633s (± 0.69%) ~ 23.198s 23.996s p=0.347 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 334,953 336,259 +1,306 (+ 0.39%) ~ ~ p=0.000 n=12
Types 197,616 201,520 +3,904 (+ 1.98%) ~ ~ p=0.000 n=12
Memory Used 313,258k (± 0.02%) 314,862k (± 0.03%) +1,604k (+ 0.51%) 314,659k 315,126k p=0.000 n=12
Memory Allocs 4,623,651 (± 0.01%) 4,637,069 (± 0.01%) +13,418 (+ 0.29%) 4,634,924 4,637,871 p=0.000 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.114s (± 4.63%) 0.114s (± 5.00%) ~ 0.100s 0.130s p=0.966 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.050s (± 0.52%) 1.055s (± 0.36%) ~ 1.045s 1.066s p=0.246 n=12
Emit Time 0.140s (±11.74%) 0.126s (±12.55%) ~ 0.100s 0.168s p=0.325 n=12
Total Time 1.357s (± 1.26%) 1.352s (± 0.95%) ~ 1.323s 1.383s p=0.560 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 303,834 305,049 +1,215 (+ 0.40%) ~ ~ p=0.000 n=12
Types 181,663 185,425 +3,762 (+ 2.07%) ~ ~ p=0.000 n=12
Memory Used 273,936k (± 0.02%) 275,311k (± 0.02%) +1,374k (+ 0.50%) 275,201k 275,543k p=0.000 n=12
Memory Allocs 1,615,929 (± 0.01%) 1,629,712 (± 0.02%) +13,783 (+ 0.85%) 1,629,137 1,630,454 p=0.000 n=12
Config Time 0.000s 0.000s (±146.91%) ~ 0.000s 0.001s p=0.478 n=12
Parse Time 0.100s (± 3.02%) 0.101s (± 3.96%) ~ 0.091s 0.114s p=0.659 n=12
Bind Time 0.037s (±13.63%) 0.036s (±10.94%) ~ 0.028s 0.047s p=0.987 n=12
Check Time 0.828s (± 0.56%) 0.830s (± 0.64%) ~ 0.821s 0.846s p=0.876 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.980s (± 0.80%) 0.983s (± 1.05%) ~ 0.963s 1.009s p=0.639 n=12
vscode - native
Errors 380 380 ~ ~ ~ p=1.000 n=12
Symbols 11,123,441 11,130,334 +6,893 (+ 0.06%) ~ ~ p=0.000 n=12
Types 3,686,393 3,728,322 +41,929 (+ 1.14%) ~ ~ p=0.000 n=12
Memory Used 7,127,150k (± 0.01%) 7,141,723k (± 0.02%) +14,573k (+ 0.20%) 7,138,568k 7,144,118k p=0.000 n=12
Memory Allocs 54,647,398 (± 0.01%) 54,766,078 (± 0.01%) +118,680 (+ 0.22%) 54,756,768 54,789,920 p=0.000 n=12
Config Time 0.074s (± 0.53%) 0.074s (± 0.49%) ~ 0.073s 0.075s p=0.870 n=12
Parse Time 1.810s (± 5.75%) 1.806s (± 3.36%) ~ 1.666s 2.004s p=0.523 n=12
Bind Time 0.662s (±18.88%) 0.588s (±15.39%) ~ 0.447s 0.876s p=0.541 n=12
Check Time 14.180s (± 1.50%) 14.367s (± 1.55%) ~ 13.918s 14.775s p=0.062 n=12
Emit Time 5.327s (± 9.71%) 4.941s (±11.75%) ~ 4.096s 6.160s p=0.319 n=12
Total Time 22.140s (± 1.53%) 21.863s (± 1.88%) ~ 21.244s 22.766s p=0.319 n=12
webpack - native
Errors 589 589 ~ ~ ~ p=1.000 n=12
Symbols 1,302,905 1,303,729 +824 (+ 0.06%) ~ ~ p=0.000 n=12
Types 618,533 621,278 +2,745 (+ 0.44%) ~ ~ p=0.000 n=12
Memory Used 1,010,981k (± 0.02%) 1,011,922k (± 0.01%) +941k (+ 0.09%) 1,011,664k 1,012,089k p=0.000 n=12
Memory Allocs 6,595,437 (± 0.02%) 6,606,104 (± 0.05%) +10,668 (+ 0.16%) 6,602,158 6,621,750 p=0.000 n=12
Config Time 0.009s 0.009s ~ ~ ~ p=1.000 n=12
Parse Time 0.290s (± 3.50%) 0.297s (± 2.46%) ~ 0.271s 0.315s p=0.247 n=12
Bind Time 0.082s (±16.47%) 0.071s (± 9.16%) ~ 0.062s 0.099s p=0.504 n=12
Check Time 2.023s (± 0.61%) 2.032s (± 0.79%) ~ 1.976s 2.055s p=0.091 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 2.439s (± 0.67%) 2.445s (± 0.81%) ~ 2.382s 2.499s p=0.809 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,055,452 1,060,768 +5,316 (+ 0.50%) ~ ~ p=0.000 n=12
Types 386,738 389,848 +3,110 (+ 0.80%) ~ ~ p=0.000 n=12
Memory Used 613,361k (± 0.03%) 615,681k (± 0.03%) +2,320k (+ 0.38%) 615,295k 616,271k p=0.000 n=12
Memory Allocs 4,862,964 (± 0.04%) 4,907,231 (± 0.11%) +44,267 (+ 0.91%) 4,896,581 4,926,204 p=0.000 n=12
Config Time 0.003s 0.003s ~ ~ ~ p=1.000 n=12
Parse Time 0.141s (± 3.53%) 0.143s (± 2.59%) ~ 0.136s 0.154s p=0.485 n=12
Bind Time 0.047s (±18.37%) 0.040s (±10.57%) ~ 0.032s 0.051s p=0.353 n=12
Check Time 1.138s (± 0.48%) 1.157s (± 0.67%) +0.019s (+ 1.68%) 1.143s 1.184s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.339s (± 0.86%) 1.355s (± 0.89%) ~ 1.335s 1.397s p=0.109 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

@weswigham

Wesley Wigham (weswigham) commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Man, when we tried this in the JS codebase, the type comparison on every possible inference target ended up blowing up any savings we got from skipping deeper inferences on less appropriate members. But the perf suite shows it as good now, so I'm a fan. Maybe the optional prop fastpath and objecty type carve-outs are saving enough low-value comparisons it's at least perf neutral again? I'd be happier if we used discriminateTypeByDiscriminableItems (and maybe optimized it a bit to match), though, so "the method of discrimination" always matches. areTypesComparable is interesting, considering we normally discriminate using assignability - thought I guess we're kinda inverting the normal check here - "skip incompatible" instead of "select only compatible". In theory those are compliments, though.

@typescript-automation

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the top 400 repos with tsc comparing baseline and pr:

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

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

TS2322: const type parameter infers readonly [], rejected through a union return type (7.1.0-dev regression from typescript-go#4591)

4 participants