Skip to content

Optimize narrowTypeByEquality and narrowTypeBySwitchOnDiscriminant - #4781

Open
ahejlsberg wants to merge 1 commit into
mainfrom
cfa-narrowing-optimization
Open

Optimize narrowTypeByEquality and narrowTypeBySwitchOnDiscriminant#4781
ahejlsberg wants to merge 1 commit into
mainfrom
cfa-narrowing-optimization

Conversation

@ahejlsberg

Copy link
Copy Markdown
Member

Inspired by the performance gains in #4711, this PR further optimizes union type narrowing in control flow analysis.

Copilot AI review requested due to automatic review settings July 28, 2026 21:26
@ahejlsberg

Copy link
Copy Markdown
Member Author

@typescript-bot test it

@typescript-automation

typescript-automation Bot commented Jul 28, 2026

Copy link
Copy Markdown

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

Command Status Results
test top400 ✅ 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.

Pull request overview

Optimizes primitive-union narrowing during control-flow analysis.

Changes:

  • Adds fast paths for equality and switch narrowing.
  • Optimizes type removal and centralizes union membership checks.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
internal/checker/flow.go Adds narrowing fast paths.
internal/checker/checker.go Optimizes union helpers and type removal.

Comment thread internal/checker/flow.go
Comment on lines +601 to +603
filteredType := c.removeType(t, c.getRegularTypeOfLiteralType(valueType))
if filteredType != t {
return filteredType
Comment thread internal/checker/flow.go
Comment on lines +589 to +594
if !doubleEquals && t.objectFlags&ObjectFlagsPrimitiveUnion != 0 && valueType.flags&TypeFlagsPrimitive != 0 {
regularType := c.getRegularTypeOfLiteralType(valueType)
if c.unionContainsType(t, regularType) {
return regularType
}
}
Comment thread internal/checker/flow.go
Comment on lines +1123 to +1128
if t.objectFlags&ObjectFlagsPrimitiveUnion != 0 && discriminantType.flags&TypeFlagsPrimitive != 0 {
regularType := c.getRegularTypeOfLiteralType(discriminantType)
if c.unionContainsType(t, regularType) {
caseType = regularType
}
}
@typescript-automation

Copy link
Copy Markdown

@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 4 4 ~ ~ ~ p=1.000 n=6
Symbols 81,795 (± 0.02%) 81,829 (± 0.02%) +35 (+ 0.04%) 81,801 81,858 p=0.013 n=6
Types 98,817 98,817 ~ ~ ~ p=1.000 n=6
Memory Used 173,358k (± 0.48%) 173,758k (± 0.70%) ~ 171,734k 175,061k p=0.471 n=6
Memory Allocs 2,694,527 (± 0.01%) 2,686,094 (± 0.01%) -8,433 (- 0.31%) 2,685,696 2,686,313 p=0.005 n=6
Config Time 0.000s (±244.70%) 0.000s (±244.70%) ~ 0.000s 0.001s p=1.000 n=6
Parse Time 0.061s (±13.53%) 0.056s (±12.14%) ~ 0.049s 0.069s p=0.259 n=6
Bind Time 0.017s (±13.13%) 0.019s (±34.10%) ~ 0.013s 0.032s p=0.571 n=6
Check Time 0s 0s ~ ~ ~ p=1.000 n=6
Emit Time 0.820s (± 0.56%) 0.815s (± 0.73%) ~ 0.808s 0.824s p=0.149 n=6
Total Time 0.899s (± 0.97%) 0.892s (± 1.34%) ~ 0.878s 0.913s p=0.108 n=6
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=6
Symbols 871,302 (± 0.14%) 872,158 (± 0.16%) ~ 869,607 873,509 p=0.378 n=6
Types 263,961 (± 0.00%) 263,962 (± 0.00%) ~ 263,960 263,965 p=0.564 n=6
Memory Used 812,489k (± 0.03%) 812,589k (± 0.02%) ~ 812,192k 812,702k p=0.378 n=6
Memory Allocs 13,145,041 (± 0.13%) 13,134,291 (± 0.04%) ~ 13,127,967 13,143,651 p=0.128 n=6
Config Time 0.017s (± 2.98%) 0.017s (± 2.38%) ~ 0.017s 0.018s p=0.595 n=6
Parse Time 0.244s (± 4.34%) 0.242s (± 4.26%) ~ 0.227s 0.259s p=0.873 n=6
Bind Time 0.045s (±23.07%) 0.052s (±33.07%) ~ 0.040s 0.079s p=0.370 n=6
Check Time 0s 0s ~ ~ ~ p=1.000 n=6
Emit Time 1.896s (± 2.19%) 1.897s (± 1.28%) ~ 1.851s 1.916s p=1.000 n=6
Total Time 2.213s (± 1.63%) 2.219s (± 0.94%) ~ 2.180s 2.241s p=0.810 n=6
mui-docs - native
Errors 11,275 (± 0.13%) 11,281 (± 0.01%) ~ 11,279 11,282 p=0.797 n=6
Symbols 4,447,880 4,447,880 ~ ~ ~ p=1.000 n=6
Types 1,662,215 1,662,215 ~ ~ ~ p=1.000 n=6
Memory Used 5,053,044k (± 0.04%) 5,053,470k (± 0.01%) ~ 5,052,600k 5,054,485k p=0.936 n=6
Memory Allocs 51,349,959 (± 0.08%) 51,332,091 (± 0.07%) ~ 51,289,014 51,379,547 p=0.689 n=6
Config Time 0.016s (± 3.16%) 0.017s (± 7.26%) ~ 0.016s 0.019s p=0.923 n=6
Parse Time 0.520s (± 2.25%) 0.528s (± 2.37%) ~ 0.510s 0.542s p=0.298 n=6
Bind Time 0.002s 0.002s ~ ~ ~ p=1.000 n=6
Check Time 16.709s (± 0.96%) 16.676s (± 0.85%) ~ 16.564s 16.932s p=1.000 n=6
Emit Time 0.470s (± 3.43%) 0.516s (±25.78%) ~ 0.456s 0.788s p=0.688 n=6
Total Time 18.461s (± 1.03%) 18.465s (± 1.18%) ~ 18.241s 18.739s p=0.936 n=6
self-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,394,220 1,394,220 ~ ~ ~ p=1.000 n=6
Types 442,210 442,210 ~ ~ ~ p=1.000 n=6
Memory Used 1,611,398k (± 0.31%) 1,605,555k (± 0.33%) -5,843k (- 0.36%) 1,599,587k 1,615,027k p=0.045 n=6
Memory Allocs 97,100,651 (± 0.07%) 96,794,372 (± 0.06%) -306,279 (- 0.32%) 96,741,613 96,906,047 p=0.005 n=6
Config Time 0.016s (±17.42%) 0.018s (±29.64%) ~ 0.010s 0.025s p=0.465 n=6
Parse Time 0.238s (± 2.87%) 0.237s (± 2.61%) ~ 0.230s 0.246s p=0.688 n=6
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=6
Check Time 2.169s (± 0.72%) 2.109s (± 0.41%) -0.060s (- 2.76%) 2.095s 2.119s p=0.005 n=6
Emit Time 0.299s (± 3.87%) 0.300s (± 3.45%) ~ 0.282s 0.313s p=0.872 n=6
Total Time 28.304s (± 0.39%) 27.507s (± 0.75%) -0.797s (- 2.82%) 27.155s 27.744s p=0.005 n=6
self-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 337,681 337,681 ~ ~ ~ p=1.000 n=6
Types 199,559 199,559 ~ ~ ~ p=1.000 n=6
Memory Used 320,591k (± 0.03%) 320,730k (± 0.03%) +139k (+ 0.04%) 320,603k 320,857k p=0.045 n=6
Memory Allocs 4,676,102 (± 0.02%) 4,661,662 (± 0.01%) -14,440 (- 0.31%) 4,661,179 4,662,410 p=0.005 n=6
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=6
Parse Time 0.114s (± 3.94%) 0.114s (± 2.42%) ~ 0.111s 0.119s p=0.868 n=6
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=6
Check Time 1.282s (± 1.43%) 1.222s (± 0.77%) 🟩-0.059s (- 4.63%) 1.212s 1.234s p=0.005 n=6
Emit Time 0.115s (± 7.36%) 0.122s (± 6.51%) ~ 0.113s 0.137s p=0.108 n=6
Total Time 1.569s (± 1.28%) 1.508s (± 1.02%) 🟩-0.061s (- 3.92%) 1.489s 1.530s p=0.005 n=6
ts-pre-modules - native
Errors 3 3 ~ ~ ~ p=1.000 n=6
Symbols 97,488 97,488 ~ ~ ~ p=1.000 n=6
Types 356 356 ~ ~ ~ p=1.000 n=6
Memory Used 128,877k (± 0.01%) 128,903k (± 0.02%) +27k (+ 0.02%) 128,886k 128,958k p=0.031 n=6
Memory Allocs 177,864 (± 0.24%) 177,424 (± 0.07%) ~ 177,263 177,534 p=0.093 n=6
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=6
Parse Time 0.102s (± 4.48%) 0.105s (± 5.77%) ~ 0.096s 0.115s p=0.294 n=6
Bind Time 0.037s (±11.07%) 0.041s (± 7.23%) ~ 0.037s 0.044s p=0.147 n=6
Check Time 0s 0s ~ ~ ~ p=1.000 n=6
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=6
Total Time 0.143s (± 3.78%) 0.149s (± 4.04%) ~ 0.139s 0.155s p=0.173 n=6
vscode - native
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 7,454,611 7,454,611 ~ ~ ~ p=1.000 n=6
Types 2,602,333 2,602,328 -5 (- 0.00%) ~ ~ p=0.001 n=6
Memory Used 5,025,743k (± 0.02%) 5,025,215k (± 0.01%) ~ 5,024,804k 5,026,304k p=0.128 n=6
Memory Allocs 38,097,508 (± 0.09%) 38,027,882 (± 0.13%) -69,626 (- 0.18%) 38,000,252 38,126,979 p=0.045 n=6
Config Time 0.072s (± 6.03%) 0.073s (±12.22%) ~ 0.066s 0.090s p=0.872 n=6
Parse Time 0.919s (± 3.59%) 0.940s (± 5.39%) ~ 0.887s 1.032s p=0.689 n=6
Bind Time 0.183s (±29.58%) 0.161s (± 2.11%) ~ 0.158s 0.167s p=0.416 n=6
Check Time 8.422s (± 0.85%) 8.368s (± 0.39%) ~ 8.340s 8.426s p=0.173 n=6
Emit Time 2.442s (± 7.18%) 2.559s (± 4.78%) ~ 2.453s 2.743s p=0.423 n=6
Total Time 12.060s (± 0.96%) 12.118s (± 0.97%) ~ 11.959s 12.270s p=0.471 n=6
webpack - native
Errors 2 2 ~ ~ ~ p=1.000 n=6
Symbols 193,769 193,769 ~ ~ ~ p=1.000 n=6
Types 340 340 ~ ~ ~ p=1.000 n=6
Memory Used 229,991k (± 0.16%) 230,177k (± 0.08%) ~ 229,846k 230,377k p=0.336 n=6
Memory Allocs 974,035 (± 0.79%) 977,511 (± 0.68%) ~ 968,848 987,982 p=0.471 n=6
Config Time 0.012s (±17.55%) 0.010s (±14.55%) ~ 0.009s 0.012s p=0.157 n=6
Parse Time 0.149s (± 4.26%) 0.144s (± 4.29%) ~ 0.133s 0.150s p=0.228 n=6
Bind Time 0s 0s ~ ~ ~ p=1.000 n=6
Check Time 0s 0s ~ ~ ~ p=1.000 n=6
Emit Time 0.041s (±22.05%) 0.041s (±13.95%) ~ 0.036s 0.048s p=0.686 n=6
Total Time 0.202s (± 3.12%) 0.196s (± 4.87%) ~ 0.179s 0.203s p=0.297 n=6
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,064,618 1,064,618 ~ ~ ~ p=1.000 n=6
Types 392,776 392,776 ~ ~ ~ p=1.000 n=6
Memory Used 637,894k (± 0.02%) 638,074k (± 0.02%) +181k (+ 0.03%) 637,936k 638,195k p=0.045 n=6
Memory Allocs 4,993,724 (± 0.25%) 4,989,485 (± 0.06%) ~ 4,984,899 4,994,096 p=1.000 n=6
Config Time 0.005s 0.005s ~ ~ ~ p=1.000 n=6
Parse Time 0.133s (± 2.56%) 0.126s (± 3.09%) 🟩-0.007s (- 5.51%) 0.121s 0.131s p=0.016 n=6
Bind Time 0.038s (±27.93%) 0.032s (±26.82%) ~ 0.024s 0.046s p=0.195 n=6
Check Time 1.233s (± 0.61%) 1.243s (± 0.78%) ~ 1.234s 1.254s p=0.173 n=6
Emit Time 0.001s 0.001s ~ ~ ~ p=1.000 n=6
Total Time 1.415s (± 0.54%) 1.411s (± 0.65%) ~ 1.397s 1.422s p=0.421 n=6
System info unknown
Hosts
  • native
Scenarios
  • Compiler-Unions - native
  • angular-1 - native
  • mui-docs - native
  • self-build-src - native
  • self-compiler - native
  • ts-pre-modules - native
  • vscode - native
  • webpack - native
  • xstate-main - native
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-automation

Copy link
Copy Markdown

@ahejlsberg Here are the results of running the top 400 repos with tsc comparing main and refs/pull/4781/merge:

Everything looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants