Skip to content

Resolve class and interface members through shared layouts - #64765

Draft
Jake Bailey (jakebailey) wants to merge 18 commits into
microsoft:mainfrom
jakebailey:shared-member-layouts
Draft

Jake Bailey (jakebailey) wants to merge 18 commits into
microsoft:mainfrom
jakebailey:shared-member-layouts

Conversation

@jakebailey

@jakebailey Jake Bailey (jakebailey) commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

We currently resolve the members of an instantiated class or interface eagerly. The first time anything touches an instantiation such as Array<string>, we instantiate every declared member, copy in every inherited member from its base types, and build a fresh symbol table and property list. This is wasteful because most instantiations only ever have a few of their members accessed. Meanwhile, the names, order, and flags of the members are identical in every instantiation; only their types differ. On large projects, these mostly unused symbols and tables are a large share of checker memory.

This PR computes each class or interface's member names, order, and origins once, as a member layout that all of its instantiations share. A member's origin is one of three things: a declared member, a slot in a base type's layout, or a property of a base type looked up by name. Each instantiation holds only a slot array, and a slot is filled the first time that member is accessed. Every member goes through one code path, getLazyMember. Looking up a member by name resolves one slot, and full resolution (Properties(), getMembers()) resolves every slot, so property order and semantics don't change. Callers that need only the count, names, or flags of properties (isWeakType, getUnmatchedProperties, isEmptyResolvedType, etc.) read the layout and don't resolve anything.

Compared to main, VS Code uses 13.7% less memory and creates 47% fewer symbols; xstate uses 15.7% less memory and creates 40% fewer symbols.

Anonymous types (instantiated type literals, object literals) still resolve their members eagerly. I tried giving them shared layouts too, but nearly all of their members end up being used and they don't keep a separate symbol table today. On VS Code, that cost about 30MB more memory for almost no symbol savings.

This is the third attempt at avoiding eager member instantiation, after #64475 and #64735. Both of those keep a lazy path for individual lookups alongside the existing eager path for full resolution, so the two must agree. The feedback on them was that full resolution should simply resolve every member through a single code path. Here, the shared layout is what makes that possible: the names, order, and origins of members are known before any member is resolved, so one per-member path serves both a single lookup and full resolution.

Performance

From the perf bot (baseline is main, n=12):

Project Memory used Symbols Allocs Check time Total time
Compiler-Unions -4.17% -23.23% -0.45% -2.25% -2.24%
angular-1 -7.30% -30.32% -0.47% ~ -3.34%
mui-docs -18.07% -11.64% -1.74% -14.30% -11.67%
strada-build-src -2.83% -14.62% -0.46% -3.42% -1.28%
strada-compiler -4.50% -21.25% -0.43% -2.31% -1.78%
ts-pre-modules -5.45% -23.67% -1.47% -2.97% -2.91%
vscode -13.57% -46.67% -1.76% -7.93% -8.12%
webpack -10.82% -30.35% -2.50% -11.23% -9.34%
xstate-main -14.76% -39.59% -2.80% -11.86% -8.94%

Every instantiation of a generic class or interface eagerly built a
full symbol table and instantiated every own and inherited member,
even though most code only ever looks at a handful of them. On large
projects this dominates checker memory: VS Code creates millions of
instantiated symbols that are never used.

The names, order, and flags of a class or interface's members do not
depend on its type arguments, so compute them once per target as a
layout that records where each member comes from, and share it across
instantiations. Each instantiation then only holds a slot per member,
filled on first access. There is a single path for resolving a member;
full resolution is just resolving every slot, so property order and
semantics are unchanged.

Callers that only need the count, names, or flags of properties use
the layout directly so they don't force resolution.
Copilot AI balanced review requested due to automatic review settings October 13, 2026 07:41
@jakebailey

This comment was marked as outdated.

@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 13, 2026
@typescript-automation

This comment was marked as outdated.

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.

🟡 Changes recommended

Non-layout base lookup can return an incorrect inherited symbol, and the new resolution path lacks regression coverage.

1 open finding
What changed in this PR

Introduces shared member layouts to lazily resolve class and interface members, reducing checker memory and symbol creation.

Changes:

  • Adds shared layouts and on-demand member slots.
  • Uses unresolved property shapes for count/flag checks.
  • Updates consumers to use lazy-aware accessors.
File Description
tsc/​internal/​checker/​checker.go Implements layout construction and lazy resolution.
tsc/​internal/​checker/​types.go Adds lazy member state and accessors.
tsc/​internal/​checker/​relater.go Avoids unnecessary property resolution.
tsc/​internal/​checker/​services.go Uses lazy-aware member enumeration.
tsc/​internal/​checker/​nodebuilderimpl.go Materializes members when serializing types.
tsc/​internal/​checker/​nodebuilder_hover.go Materializes properties for hover output.

🧠 Review effort: Balanced

Comment thread tsc/internal/checker/checker.go
@typescript-automation

This comment was marked as outdated.

@typescript-automation

This comment was marked as outdated.

@typescript-automation

This comment was marked as outdated.

Once a statement exceeds the instantiation count limit, every further
instantiation yields the error type, including the base types of a
class or interface instance whose members are resolved afterward. The
shared layout of that class or interface still expects to find
inherited members in those bases, so resolving such a member crashed.

Without a shared layout, an error base type simply contributes no
members, matching the behavior before shared layouts.
@typescript-automation

This comment was marked as outdated.

Building a member layout allocated a fresh entry slice, name map, and
rank map every time, which made it one of the larger sources of
allocation in the checker: about 10% of all bytes allocated when
checking VS Code. Layouts are built often enough, and the scratch
storage is discarded immediately, so keep it on the checker and reuse
it instead.

On VS Code, bytes allocated by layout building drop from 503MB to
86MB, total bytes allocated from 4.97GB to 4.41GB, and allocation
count by about 2%.
@jakebailey

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) test it

@typescript-automation

typescript-automation Bot commented Oct 13, 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

@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)
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 88,498 🟩-26,778 (-23.23%) ~ ~ p=0.000 n=12
Types 79,613 79,613 ~ ~ ~ p=1.000 n=12
Memory Used 133,242k (± 0.05%) 127,681k (± 0.06%) 🟩-5,561k (- 4.17%) 127,550k 127,863k p=0.000 n=12
Memory Allocs 2,190,303 (± 0.01%) 2,180,469 (± 0.00%) -9,834 (- 0.45%) 2,180,279 2,180,878 p=0.000 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.041s (± 3.96%) 0.041s (± 3.76%) ~ 0.037s 0.044s p=0.788 n=12
Bind Time 0.012s (± 9.29%) 0.012s (±10.37%) ~ 0.010s 0.016s p=0.902 n=12
Check Time 0.500s (± 0.83%) 0.489s (± 0.68%) -0.011s (- 2.25%) 0.482s 0.499s p=0.000 n=12
Emit Time 0.271s (± 1.24%) 0.264s (± 2.13%) -0.008s (- 2.83%) 0.249s 0.282s p=0.006 n=12
Total Time 0.830s (± 0.72%) 0.811s (± 0.94%) -0.019s (- 2.24%) 0.791s 0.834s p=0.001 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 845,812 (± 0.04%) 589,381 (± 0.01%) 🟩-256,431 (-30.32%) 589,201 589,572 p=0.000 n=12
Types 248,124 (± 0.00%) 248,133 (± 0.00%) +9 (+ 0.00%) 248,131 248,135 p=0.000 n=12
Memory Used 746,088k (± 0.02%) 691,630k (± 0.02%) 🟩-54,458k (- 7.30%) 691,232k 692,047k p=0.000 n=12
Memory Allocs 12,914,282 (± 0.04%) 12,853,548 (± 0.02%) -60,735 (- 0.47%) 12,849,165 12,867,567 p=0.000 n=12
Config Time 0.017s 0.017s ~ ~ ~ p=1.000 n=12
Parse Time 0.258s (± 2.89%) 0.254s (± 2.30%) ~ 0.234s 0.267s p=0.401 n=12
Bind Time 0.060s (± 4.85%) 0.062s (±12.92%) ~ 0.056s 0.102s p=0.863 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.557s (± 0.59%) 1.493s (± 0.89%) 🟩-0.065s (- 4.16%) 1.448s 1.527s p=0.000 n=12
Total Time 1.903s (± 0.44%) 1.840s (± 0.90%) 🟩-0.063s (- 3.34%) 1.794s 1.900s p=0.000 n=12
mui-docs - native
Errors 11,405 11,405 ~ ~ ~ p=1.000 n=12
Symbols 4,339,337 3,834,053 🟩-505,284 (-11.64%) ~ ~ p=0.000 n=12
Types 1,379,387 1,379,387 ~ ~ ~ p=1.000 n=12
Memory Used 2,774,542k (± 0.06%) 2,273,308k (± 0.04%) 🟩-501,233k (-18.07%) 2,270,529k 2,276,308k p=0.000 n=12
Memory Allocs 32,738,618 (± 0.07%) 32,168,204 (± 0.06%) -570,414 (- 1.74%) 32,136,544 32,233,801 p=0.000 n=12
Config Time 0.017s 0.017s ~ ~ ~ p=1.000 n=12
Parse Time 0.484s (± 3.38%) 0.510s (± 2.82%) 🔻+0.026s (+ 5.39%) 0.480s 0.558s p=0.023 n=12
Bind Time 0.002s 0.002s ~ ~ ~ p=1.000 n=12
Check Time 8.835s (± 0.23%) 7.572s (± 0.85%) 🟩-1.263s (-14.30%) 7.452s 7.707s p=0.000 n=12
Emit Time 0.433s (± 4.75%) 0.439s (± 6.78%) +0.006s (+ 1.39%) 0.420s 0.588s p=0.005 n=12
Total Time 10.536s (± 0.35%) 9.306s (± 0.65%) 🟩-1.230s (-11.67%) 9.187s 9.446s p=0.000 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,388,669 1,185,704 🟩-202,965 (-14.62%) ~ ~ p=0.000 n=12
Types 441,862 441,862 ~ ~ ~ p=1.000 n=12
Memory Used 1,617,388k (± 0.73%) 1,571,538k (± 1.02%) -45,850k (- 2.83%) 1,542,202k 1,608,277k p=0.000 n=12
Memory Allocs 92,184,990 (± 0.12%) 91,759,752 (± 0.20%) -425,239 (- 0.46%) 91,238,746 92,104,790 p=0.000 n=12
Config Time 0.003s (±16.75%) 0.004s (± 6.39%) ~ 0.003s 0.004s p=0.155 n=12
Parse Time 0.192s (± 3.40%) 0.198s (± 3.41%) ~ 0.182s 0.218s p=0.213 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.813s (± 0.50%) 1.751s (± 0.79%) 🟩-0.062s (- 3.42%) 1.716s 1.782s p=0.000 n=12
Emit Time 0.305s (± 3.18%) 0.324s (± 4.62%) 🔻+0.019s (+ 6.25%) 0.284s 0.358s p=0.050 n=12
Total Time 23.437s (± 0.74%) 23.136s (± 0.73%) -0.301s (- 1.28%) 22.775s 23.550s p=0.012 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 334,953 263,776 🟩-71,177 (-21.25%) ~ ~ p=0.000 n=12
Types 197,616 197,616 ~ ~ ~ p=1.000 n=12
Memory Used 296,080k (± 0.03%) 282,771k (± 0.03%) 🟩-13,309k (- 4.50%) 282,551k 282,966k p=0.000 n=12
Memory Allocs 4,621,702 (± 0.01%) 4,601,916 (± 0.01%) -19,786 (- 0.43%) 4,601,005 4,602,962 p=0.000 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.112s (± 5.89%) 0.116s (± 2.89%) ~ 0.108s 0.123s p=0.325 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.039s (± 0.53%) 1.015s (± 0.47%) -0.024s (- 2.31%) 1.005s 1.031s p=0.000 n=12
Emit Time 0.152s (± 4.43%) 0.147s (± 6.40%) ~ 0.123s 0.173s p=0.504 n=12
Total Time 1.356s (± 0.56%) 1.332s (± 1.03%) -0.024s (- 1.78%) 1.295s 1.362s p=0.006 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 303,834 231,913 🟩-71,921 (-23.67%) ~ ~ p=0.000 n=12
Types 181,663 181,663 ~ ~ ~ p=1.000 n=12
Memory Used 256,648k (± 0.01%) 242,659k (± 0.02%) 🟩-13,989k (- 5.45%) 242,495k 242,718k p=0.000 n=12
Memory Allocs 1,613,811 (± 0.01%) 1,590,168 (± 0.01%) -23,643 (- 1.47%) 1,589,496 1,590,619 p=0.000 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.098s (± 5.17%) 0.098s (± 4.38%) ~ 0.085s 0.107s p=0.831 n=12
Bind Time 0.041s (±11.89%) 0.038s (±12.90%) ~ 0.027s 0.053s p=0.368 n=12
Check Time 0.812s (± 0.71%) 0.788s (± 0.61%) -0.024s (- 2.97%) 0.777s 0.799s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.966s (± 0.88%) 0.938s (± 0.71%) -0.028s (- 2.91%) 0.916s 0.950s p=0.000 n=12
vscode - native
Errors 396 396 ~ ~ ~ p=1.000 n=12
Symbols 11,300,551 6,026,058 🟩-5,274,493 (-46.67%) ~ ~ p=0.000 n=12
Types 3,725,313 3,725,313 ~ ~ ~ p=1.000 n=12
Memory Used 6,453,141k (± 0.01%) 5,577,199k (± 0.02%) 🟩-875,942k (-13.57%) 5,574,796k 5,579,493k p=0.000 n=12
Memory Allocs 55,473,274 (± 0.03%) 54,494,667 (± 0.02%) -978,608 (- 1.76%) 54,480,323 54,534,515 p=0.000 n=12
Config Time 0.075s (± 0.43%) 0.075s (± 0.33%) ~ 0.075s 0.076s p=0.371 n=12
Parse Time 1.833s (± 3.73%) 1.852s (± 3.10%) ~ 1.697s 1.971s p=0.504 n=12
Bind Time 0.669s (±18.45%) 0.569s (±19.61%) ~ 0.447s 0.925s p=0.340 n=12
Check Time 13.662s (± 0.95%) 12.578s (± 0.92%) 🟩-1.084s (- 7.93%) 12.291s 12.784s p=0.000 n=12
Emit Time 5.456s (± 8.70%) 4.857s (± 7.64%) ~ 4.267s 6.057s p=0.066 n=12
Total Time 21.787s (± 1.87%) 20.018s (± 1.63%) 🟩-1.768s (- 8.12%) 19.398s 20.952s p=0.000 n=12
webpack - native
Errors 585 585 ~ ~ ~ p=1.000 n=12
Symbols 1,304,467 908,614 🟩-395,853 (-30.35%) ~ ~ p=0.000 n=12
Types 608,716 608,716 ~ ~ ~ p=1.000 n=12
Memory Used 933,527k (± 0.02%) 832,490k (± 0.02%) 🟩-101,037k (-10.82%) 832,126k 832,863k p=0.000 n=12
Memory Allocs 6,622,596 (± 0.01%) 6,456,801 (± 0.05%) -165,795 (- 2.50%) 6,453,092 6,470,369 p=0.000 n=12
Config Time 0.009s 0.009s ~ ~ ~ p=1.000 n=12
Parse Time 0.300s (± 2.67%) 0.306s (± 3.18%) ~ 0.282s 0.334s p=0.370 n=12
Bind Time 0.089s (±18.04%) 0.083s (±19.43%) ~ 0.066s 0.135s p=0.702 n=12
Check Time 2.123s (± 0.72%) 1.884s (± 0.81%) 🟩-0.238s (-11.23%) 1.849s 1.917s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 2.557s (± 0.37%) 2.318s (± 0.33%) 🟩-0.239s (- 9.34%) 2.295s 2.339s p=0.000 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,062,854 642,120 🟩-420,734 (-39.59%) ~ ~ p=0.000 n=12
Types 391,024 391,024 ~ ~ ~ p=1.000 n=12
Memory Used 548,207k (± 0.03%) 467,289k (± 0.02%) 🟩-80,919k (-14.76%) 466,998k 467,509k p=0.000 n=12
Memory Allocs 4,895,101 (± 0.11%) 4,758,161 (± 0.08%) -136,940 (- 2.80%) 4,748,617 4,767,343 p=0.000 n=12
Config Time 0.003s 0.003s ~ ~ ~ p=1.000 n=12
Parse Time 0.146s (± 3.34%) 0.147s (± 3.55%) ~ 0.135s 0.167s p=0.876 n=12
Bind Time 0.043s (±21.07%) 0.051s (±17.71%) ~ 0.034s 0.075s p=0.061 n=12
Check Time 1.024s (± 0.73%) 0.903s (± 1.05%) 🟩-0.121s (-11.86%) 0.882s 0.938s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.224s (± 0.78%) 1.115s (± 1.19%) 🟩-0.109s (- 8.94%) 1.087s 1.159s p=0.000 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
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) Here are the results of running the top 400 repos with tsc comparing baseline and pr:

Everything looks good!

Review feedback was that the member layout code is hard to read.
Member origins were encoded with negative sentinels in the base and
slot fields, the sort ranks were bare numbers, and callers that wanted
property names and flags without resolving members had to juggle a
pair of return values whose meaning depended on each other.

Give member origins an explicit kind, name the sort ranks, and replace
the shapes-and-lazy-members pair with getPropertyShapesOfType and
getPropertyOfTypeAt, which work the same for every type. The compact
layout representation is kept: a map index and per-member keys cost
about 4% more memory on VS Code, since every interface layout
includes all of its inherited members.

         │   before    │                after                │
         │  heap-B/op  │  heap-B/op   vs base                │
vscode     3.539Gi ± ∞   3.548Gi ± ∞  +0.25% (p=0.029 n=4)
mui        2.164Gi ± ∞   2.169Gi ± ∞  +0.23% (p=0.029 n=4)

         │  before   │               after                 │
         │ allocs/op │ allocs/op   vs base                 │
vscode     36.24M ± ∞  36.26M ± ∞       ~ (p=0.886 n=4)
mui        32.13M ± ∞  32.18M ± ∞       ~ (p=0.686 n=4)
# Conflicts:
#	tsc/internal/checker/checker.go
#	tsc/internal/checker/nodebuilderimpl.go
#	tsc/internal/checker/relater.go
#	tsc/internal/checker/types.go
Lazily resolved members add one pointer to every structured type.
@jakebailey

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) perf test this faster

@typescript-automation

typescript-automation Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

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

Command Status Results
perf test this faster ✅ Started 👀 Results

@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 88,498 🟩-26,778 (-23.23%) ~ ~ p=0.000 n=12
Types 79,613 79,613 ~ ~ ~ p=1.000 n=12
Memory Used 113,507k (± 0.08%) 108,651k (± 0.04%) 🟩-4,856k (- 4.28%) 108,571k 108,787k p=0.000 n=12
Memory Allocs 2,212,436 (± 0.01%) 2,200,147 (± 0.00%) -12,290 (- 0.56%) 2,199,917 2,200,350 p=0.000 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.039s (± 4.97%) 0.040s (± 2.57%) ~ 0.038s 0.043s p=0.518 n=12
Bind Time 0.014s (±11.99%) 0.014s (±10.07%) ~ 0.011s 0.018s p=0.895 n=12
Check Time 0.467s (± 0.81%) 0.470s (± 0.88%) ~ 0.463s 0.487s p=0.270 n=12
Emit Time 0.270s (± 1.68%) 0.265s (± 2.37%) ~ 0.249s 0.281s p=0.296 n=12
Total Time 0.798s (± 0.64%) 0.796s (± 1.20%) ~ 0.779s 0.828s p=0.523 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 847,281 (± 0.08%) 586,954 (± 0.02%) 🟩-260,327 (-30.73%) 586,509 587,280 p=0.000 n=12
Types 247,032 (± 0.00%) 247,040 (± 0.00%) +8 (+ 0.00%) 247,037 247,042 p=0.000 n=12
Memory Used 596,488k (± 0.04%) 548,229k (± 0.03%) 🟩-48,258k (- 8.09%) 547,641k 548,591k p=0.000 n=12
Memory Allocs 13,140,105 (± 0.02%) 13,056,539 (± 0.05%) -83,566 (- 0.64%) 13,048,847 13,091,025 p=0.000 n=12
Config Time 0.016s (± 1.90%) 0.017s (± 1.99%) ~ 0.016s 0.017s p=0.680 n=12
Parse Time 0.218s (± 1.78%) 0.218s (± 1.80%) ~ 0.210s 0.235s p=0.765 n=12
Bind Time 0.087s (±14.18%) 0.092s (± 9.29%) ~ 0.063s 0.113s p=0.766 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.542s (± 1.82%) 1.507s (± 0.80%) ~ 1.489s 1.555s p=0.089 n=12
Total Time 1.882s (± 1.72%) 1.858s (± 0.61%) ~ 1.840s 1.904s p=0.097 n=12
mui-docs - native
Errors 11,405 11,405 ~ ~ ~ p=1.000 n=12
Symbols 4,331,835 3,829,690 🟩-502,145 (-11.59%) ~ ~ p=0.000 n=12
Types 1,370,520 1,370,520 ~ ~ ~ p=1.000 n=12
Memory Used 2,625,592k (± 0.05%) 2,141,852k (± 0.08%) 🟩-483,740k (-18.42%) 2,138,254k 2,149,088k p=0.000 n=12
Memory Allocs 27,715,426 (± 0.09%) 27,141,415 (± 0.05%) -574,011 (- 2.07%) 27,113,857 27,174,144 p=0.000 n=12
Config Time 0.017s (± 1.95%) 0.017s (± 1.07%) ~ 0.016s 0.017s p=0.155 n=12
Parse Time 0.479s (± 3.51%) 0.475s (± 1.80%) ~ 0.453s 0.493s p=0.486 n=12
Bind Time 0.002s 0.002s (± 9.47%) ~ 0.001s 0.002s p=1.000 n=12
Check Time 7.244s (± 0.53%) 6.310s (± 0.50%) 🟩-0.934s (-12.90%) 6.272s 6.451s p=0.000 n=12
Emit Time 0.497s (±13.50%) 0.472s (± 1.97%) ~ 0.425s 0.479s p=0.213 n=12
Total Time 8.950s (± 1.06%) 8.034s (± 0.29%) 🟩-0.915s (-10.23%) 8.001s 8.115s p=0.000 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,388,669 1,185,704 🟩-202,965 (-14.62%) ~ ~ p=0.000 n=12
Types 441,862 441,862 ~ ~ ~ p=1.000 n=12
Memory Used 1,304,868k (± 0.53%) 1,267,986k (± 0.46%) -36,882k (- 2.83%) 1,256,329k 1,286,225k p=0.000 n=12
Memory Allocs 78,228,115 (± 0.09%) 77,805,619 (± 0.05%) -422,497 (- 0.54%) 77,671,642 77,866,947 p=0.000 n=12
Config Time 0.004s (±12.12%) 0.003s (±16.58%) ~ 0.002s 0.005s p=0.880 n=12
Parse Time 0.258s (± 2.70%) 0.253s (± 1.93%) ~ 0.240s 0.263s p=0.369 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 1.673s (± 0.40%) 1.624s (± 0.62%) -0.049s (- 2.93%) 1.596s 1.649s p=0.000 n=12
Emit Time 0.304s (± 3.21%) 0.315s (± 4.09%) ~ 0.289s 0.344s p=0.147 n=12
Total Time 19.959s (± 0.58%) 19.672s (± 0.74%) -0.287s (- 1.44%) 19.174s 20.096s p=0.002 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 334,953 263,776 🟩-71,177 (-21.25%) ~ ~ p=0.000 n=12
Types 197,616 197,616 ~ ~ ~ p=1.000 n=12
Memory Used 247,557k (± 0.03%) 235,953k (± 0.03%) 🟩-11,603k (- 4.69%) 235,708k 236,095k p=0.000 n=12
Memory Allocs 4,329,752 (± 0.01%) 4,303,535 (± 0.02%) -26,216 (- 0.61%) 4,302,173 4,307,377 p=0.000 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.110s (± 4.33%) 0.109s (± 5.11%) ~ 0.098s 0.131s p=0.765 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 0.965s (± 0.57%) 0.944s (± 0.44%) -0.021s (- 2.17%) 0.935s 0.958s p=0.000 n=12
Emit Time 0.144s (± 3.27%) 0.149s (± 3.46%) ~ 0.137s 0.168s p=0.202 n=12
Total Time 1.271s (± 0.84%) 1.257s (± 0.62%) -0.014s (- 1.11%) 1.239s 1.277s p=0.040 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 303,834 231,913 🟩-71,921 (-23.67%) ~ ~ p=0.000 n=12
Types 181,663 181,663 ~ ~ ~ p=1.000 n=12
Memory Used 215,233k (± 0.02%) 203,052k (± 0.02%) 🟩-12,181k (- 5.66%) 202,948k 203,164k p=0.000 n=12
Memory Allocs 1,401,434 (± 0.01%) 1,370,663 (± 0.01%) -30,772 (- 2.20%) 1,370,219 1,371,318 p=0.000 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.096s (± 4.61%) 0.098s (± 3.98%) ~ 0.091s 0.109s p=0.384 n=12
Bind Time 0.044s (± 7.85%) 0.040s (± 8.35%) ~ 0.031s 0.052s p=0.145 n=12
Check Time 0.744s (± 0.81%) 0.731s (± 0.77%) -0.013s (- 1.78%) 0.713s 0.746s p=0.002 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.900s (± 0.61%) 0.884s (± 0.52%) -0.016s (- 1.73%) 0.874s 0.894s p=0.000 n=12
vscode - native
Errors 396 396 ~ ~ ~ p=1.000 n=12
Symbols 11,687,338 6,235,243 🟩-5,452,095 (-46.65%) ~ ~ p=0.000 n=12
Types 3,851,240 3,851,240 ~ ~ ~ p=1.000 n=12
Memory Used 5,513,775k (± 0.01%) 4,717,391k (± 0.03%) 🟩-796,384k (-14.44%) 4,714,978k 4,724,860k p=0.000 n=12
Memory Allocs 56,191,133 (± 0.01%) 54,614,669 (± 0.03%) -1,576,465 (- 2.81%) 54,602,212 54,695,782 p=0.000 n=12
Config Time 0.075s (± 0.38%) 0.075s (± 0.24%) ~ 0.074s 0.075s p=0.590 n=12
Parse Time 1.729s (± 2.55%) 1.760s (± 2.15%) ~ 1.667s 1.865s p=0.242 n=12
Bind Time 0.654s (±15.44%) 0.589s (±13.01%) ~ 0.475s 0.816s p=0.340 n=12
Check Time 13.365s (± 1.05%) 12.295s (± 0.74%) 🟩-1.070s (- 8.01%) 11.983s 12.434s p=0.000 n=12
Emit Time 4.981s (± 8.25%) 4.917s (± 8.52%) ~ 4.277s 5.907s p=0.964 n=12
Total Time 20.887s (± 1.67%) 19.721s (± 2.14%) 🟩-1.167s (- 5.59%) 19.134s 20.706s p=0.002 n=12
webpack - native
Errors 584 584 ~ ~ ~ p=1.000 n=12
Symbols 1,299,351 896,823 🟩-402,528 (-30.98%) ~ ~ p=0.000 n=12
Types 598,360 598,360 ~ ~ ~ p=1.000 n=12
Memory Used 789,587k (± 0.02%) 691,776k (± 0.02%) 🟩-97,811k (-12.39%) 691,375k 692,215k p=0.000 n=12
Memory Allocs 6,052,601 (± 0.02%) 5,840,840 (± 0.02%) 🟩-211,761 (- 3.50%) 5,838,113 5,844,082 p=0.000 n=12
Config Time 0.009s 0.009s ~ ~ ~ p=1.000 n=12
Parse Time 0.294s (± 3.03%) 0.292s (± 2.86%) ~ 0.273s 0.310s p=0.660 n=12
Bind Time 0.076s (± 5.23%) 0.079s (±13.61%) ~ 0.066s 0.121s p=0.579 n=12
Check Time 1.949s (± 0.87%) 1.758s (± 0.90%) 🟩-0.191s (- 9.81%) 1.712s 1.791s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 2.366s (± 0.59%) 2.175s (± 0.65%) 🟩-0.192s (- 8.10%) 2.138s 2.208s p=0.000 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,062,854 642,120 🟩-420,734 (-39.59%) ~ ~ p=0.000 n=12
Types 390,845 390,845 ~ ~ ~ p=1.000 n=12
Memory Used 472,269k (± 0.02%) 400,017k (± 0.03%) 🟩-72,252k (-15.30%) 399,799k 400,453k p=0.000 n=12
Memory Allocs 4,225,745 (± 0.10%) 4,047,372 (± 0.12%) 🟩-178,373 (- 4.22%) 4,036,561 4,059,700 p=0.000 n=12
Config Time 0.003s 0.003s ~ ~ ~ p=1.000 n=12
Parse Time 0.135s (± 3.72%) 0.138s (± 3.73%) ~ 0.124s 0.150s p=0.247 n=12
Bind Time 0.050s (±17.17%) 0.044s (±15.06%) ~ 0.035s 0.066s p=0.383 n=12
Check Time 1.004s (± 0.83%) 0.869s (± 0.50%) 🟩-0.135s (-13.45%) 0.859s 0.884s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.200s (± 0.96%) 1.061s (± 0.69%) 🟩-0.139s (-11.61%) 1.042s 1.080s p=0.000 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

@jakebailey
Jake Bailey (jakebailey) requested a balanced review from Copilot October 14, 2026 18:47

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.

🟡 Changes recommended

Module cloning can lose properties, and cached inherited slots can become invalid after instantiation-limit fallback.

3 open findings

🧠 Review effort: Balanced

Comment thread tsc/internal/checker/checker.go Outdated
Comment thread tsc/internal/checker/checker.go
Namespace imports of a callable export= value whose members are
resolved lazily lose every property, so ns.value reports a missing
property. This test captures the current, incorrect output.
Members resolved through a shared layout leave the members table nil
until it is requested, so reading the field directly cloned an empty
table for namespace imports of callable export= values.
Members of class and interface instantiations are now resolved one at
a time through shared layouts, so a lookup of a single inherited member
can happen before the full member list is built. Cover lookups through
layout and non-layout bases, followed by enumeration, to check that the
members and their order match eager resolution.
When a base type's own base fails to instantiate, the base gets a
smaller layout of its own, but the derived type still reuses slot
indices from the shared layout. The derived type then reports the
wrong members. This test captures the current, incorrect output.
A shared layout addresses inherited members by their slots in each
base's shared layout. Checking only for error bases missed a base whose
own base hit the instantiation limit: that base gets a smaller layout,
so the derived type's slots pointed at the wrong members or past the
end of the base's members.

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 core caching refactor has an unresolved layout-compatibility issue and requires human review and full-suite validation.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment thread tsc/internal/checker/checker.go Outdated
Instantiations of a variadic tuple base can have different tuple
targets, and so different member layouts. The derived interface still
reuses the layout built for its first instantiation, so it loses the
added tuple elements. This test captures the current, incorrect output.
Inherited member slots index into the base's layout, so a shared
layout is only valid for instantiations whose bases resolve to the
exact base layouts it was built from. Instantiations of a variadic
tuple base produce different tuple targets with distinct layouts,
so reusing the cached layout read members from the wrong slots.

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 core member-resolution and inheritance-cache rewrite warrants human review backed by full compiler and language-server validation.

0 open findings

2 resolved since last review

🧠 Review effort: Balanced

An intersection base without type parameters can still reference
the polymorphic this type, so its members can differ between
instantiations of the derived type. Shared member layouts assumed
otherwise and crash looking up a member that no longer exists.
A computed member name that reads a member of its own class or
interface resolves the type's members while its late-bound members
are still being bound. The layout built from that partial member
table must not be reused once the late-bound members are known.
Finding inherited members by name relied on bases without type
parameters having the same members in every instantiation, which
doesn't hold when a base references the polymorphic this type. A
layout is now shared only when each base's members are known to be
fixed: the base is any or has a shared layout itself.
Late binding a member name can resolve its own type's members before
the late-bound members are known, and the shared layout built then
was reused for every later instantiation. Dropping it when the
declared members are replaced lets the next instantiation build a
layout with the complete members.
@jakebailey
Jake Bailey (jakebailey) marked this pull request as draft October 14, 2026 22:16
Each review of member layouts has found another way a shared layout
can disagree with the members its type would otherwise have. Checking
every lazily resolved class and interface type against members
resolved as before layouts finds such disagreements across whole
test suites and real projects, rather than one case at a time.

The check is enabled for now so that test runs over user and top
repositories can report mismatches.
@jakebailey

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) user test this
TypeScript Bot (@typescript-bot) test top1000

@typescript-automation

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), this PR is in an unmergable state, so is missing a merge commit to run against; please resolve conflicts and try again.

@jakebailey

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) user test this
TypeScript Bot (@typescript-bot) test top1000

@typescript-automation

typescript-automation Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

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

Command Status Results
user test this ✅ Started ✅ Results
test top1000 ✅ Started ✅ Results

@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) Here are the results of running the top 1000 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

Author: Team 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.

2 participants