Skip to content

Conversation

@Andarist
Copy link
Contributor

@AndaristAndarist commented Mar 30, 2025

@ssalbdivad found out that a chain of somewhat trivial operations can easily end up with dreaded "Type instantiation is excessively deep and possibly infinite".

Two different reproductions were created:

  1. standalone signature receiving its previous output as input in a chain: TS playground
  2. a dotted chain where a method returns a new instance of its own enclosing type alias: TS playground

We were able to bisect both to two different past PRs - one of which is quite old:

  1. the first one bisects to this PR and we can see it performing better in TS 5.4 here
  2. the other one bisects to this PR and we can see it performing better in TS 4.1 here

⚠️ that said - the "chain setup" avoids the error better but the property type lookup can still lead to the error. So, in some sense, the error just shifted place. this has been solved

I dug into this and what I found out is, those types refer to the same "base" types - they are chained from them and instantiateType repeats the work on them a lot. At least part of the problem is that getObjectTypeInstantiation calls instantiateTypes(type.aliasTypeArguments, mapper) to create part of its cache key. So it can't even check if the result is cached before it instantiates those. This problem compounds heavily in longer chains like this.

So I toyed with possible solutions and I realized that caching results on mappers could help here... to some extent at least. I don't think it's the best solution but I also don't yet understand this problem to its core to propose anything better.

It looks like this has very positive impact on instantiation counts across the test suite. It surely trades some memory for it though. It would be interesting to see perf results for this and the extended test suite run (even if only to get new data points for further investigation).

EDIT:// @ssalbdivad has tested this in some trpc-based repositories and the check time got halved for them. The current version is also able to typecheck the 50-item deep "chains" as it can be seen in the added long* tests. Those previously were only instantiable to a certain depth (smth between 15-25, depending on the test case) and the last items in the chain were being instantiated really slow. Now those longer ones are handled pretty fast.

@github-project-automationgithub-project-automationbot moved this to Not started in PR BacklogMar 30, 2025
@typescript-bottypescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Mar 30, 2025
@typescript-bot
Copy link
Collaborator

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

@jakebailey
Copy link
Member

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 30, 2025

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

CommandStatusResults
test top400✅ Started
user test this✅ Started
run dt✅ Started
perf test this faster✅ Started

@jakebailey
Copy link
Member

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 31, 2025

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

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

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user tests with tsc comparing main and refs/pull/61505/merge:

Something interesting changed - please have a look.

Details

effect

packages/effect/benchmark/tsconfig.json

tsconfig.json

tsconfig.build.json

tsconfig.base.json

packages/typeclass/dtslint/tsconfig.json

packages/platform/dtslint/tsconfig.json

packages/effect/dtslint/tsconfig.json

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors3434~~~p=1.000 n=6
Symbols62,39062,390~~~p=1.000 n=6
Types50,39550,395~~~p=1.000 n=6
Memory used194,709k (± 1.01%)196,843k (± 0.98%)~194,935k198,710kp=0.173 n=6
Parse Time1.31s (± 0.31%)1.31s (± 0.57%)~1.30s1.32sp=1.000 n=6
Bind Time0.73s0.73s~~~p=1.000 n=6
Check Time9.73s (± 0.49%)9.77s (± 0.35%)~9.71s9.81sp=0.228 n=6
Emit Time2.73s (± 0.54%)2.73s (± 0.65%)~2.71s2.75sp=0.935 n=6
Total Time14.51s (± 0.37%)14.54s (± 0.16%)~14.50s14.56sp=0.253 n=6
angular-1 - node (v18.15.0, x64)
Errors5656~~~p=1.000 n=6
Symbols948,670948,670~~~p=1.000 n=6
Types410,947410,947~~~p=1.000 n=6
Memory used1,224,296k (± 0.00%)1,248,572k (± 0.00%)+24,276k (+ 1.98%)1,248,514k1,248,618kp=0.005 n=6
Parse Time6.63s (± 1.21%)6.63s (± 0.67%)~6.56s6.70sp=1.000 n=6
Bind Time1.88s (± 0.67%)1.88s (± 0.73%)~1.87s1.90sp=0.557 n=6
Check Time31.93s (± 0.37%)32.31s (± 0.31%)+0.39s (+ 1.21%)32.18s32.47sp=0.005 n=6
Emit Time15.23s (± 0.22%)15.26s (± 0.24%)~15.21s15.32sp=0.147 n=6
Total Time55.67s (± 0.25%)56.09s (± 0.21%)+0.42s (+ 0.75%)55.98s56.31sp=0.005 n=6
mui-docs - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols2,461,2672,461,267~~~p=1.000 n=6
Types893,340893,340~~~p=1.000 n=6
Memory used2,310,804k (± 0.00%)2,342,249k (± 0.00%)+31,445k (+ 1.36%)2,342,222k2,342,302kp=0.005 n=6
Parse Time9.04s (± 0.22%)9.04s (± 0.17%)~9.02s9.06sp=1.000 n=6
Bind Time2.27s (± 0.36%)2.26s (± 0.53%)~2.25s2.28sp=0.652 n=6
Check Time75.67s (± 0.38%)75.95s (± 0.42%)~75.43s76.42sp=0.173 n=6
Emit Time0.29s (± 1.92%)0.29s (± 1.80%)~0.28s0.29sp=0.640 n=6
Total Time87.26s (± 0.34%)87.55s (± 0.35%)~87.03s87.99sp=0.173 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,226,7131,226,807+94 (+ 0.01%)~~p=0.001 n=6
Types266,991267,000+9 (+ 0.00%)~~p=0.001 n=6
Memory used2,357,616k (± 0.03%)2,376,351k (± 0.02%)+18,735k (+ 0.79%)2,375,702k2,377,087kp=0.005 n=6
Parse Time5.20s (± 1.17%)5.17s (± 0.50%)~5.14s5.21sp=0.297 n=6
Bind Time1.79s (± 1.91%)1.78s (± 0.68%)~1.77s1.80sp=0.744 n=6
Check Time35.40s (± 0.56%)35.58s (± 0.33%)~35.41s35.75sp=0.066 n=6
Emit Time2.99s (± 2.22%)2.99s (± 1.20%)~2.96s3.06sp=0.630 n=6
Total Time45.40s (± 0.72%)45.53s (± 0.17%)~45.45s45.62sp=0.066 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,226,7131,226,807+94 (+ 0.01%)~~p=0.001 n=6
Types266,991267,000+9 (+ 0.00%)~~p=0.001 n=6
Memory used2,911,961k (±12.87%)3,067,098k (± 9.90%)🔻+155,137k (+ 5.33%)2,446,535k3,191,944kp=0.031 n=6
Parse Time6.94s (± 1.45%)6.94s (± 1.80%)~6.70s7.04sp=1.000 n=6
Bind Time2.14s (± 2.28%)2.12s (± 1.06%)~2.08s2.14sp=0.810 n=6
Check Time42.65s (± 0.57%)42.85s (± 0.37%)~42.57s43.01sp=0.128 n=6
Emit Time3.45s (± 2.05%)3.53s (± 0.82%)+0.07s (+ 2.17%)3.49s3.58sp=0.044 n=6
Total Time55.20s (± 0.59%)55.45s (± 0.48%)~54.92s55.69sp=0.128 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols262,674262,691+17 (+ 0.01%)~~p=0.001 n=6
Types106,849106,858+9 (+ 0.01%)~~p=0.001 n=6
Memory used440,486k (± 0.01%)449,610k (± 0.02%)+9,124k (+ 2.07%)449,509k449,723kp=0.005 n=6
Parse Time3.54s (± 0.42%)3.51s (± 0.91%)~3.48s3.57sp=0.052 n=6
Bind Time1.31s (± 0.39%)1.32s (± 0.80%)~1.30s1.33sp=0.794 n=6
Check Time19.05s (± 0.31%)19.04s (± 0.36%)~18.93s19.13sp=0.810 n=6
Emit Time1.52s (± 0.65%)1.53s (± 0.79%)~1.51s1.54sp=0.246 n=6
Total Time25.43s (± 0.27%)25.39s (± 0.32%)~25.29s25.53sp=0.423 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors7171~~~p=1.000 n=6
Symbols225,981225,981~~~p=1.000 n=6
Types94,35694,356~~~p=1.000 n=6
Memory used371,214k (± 0.01%)379,734k (± 0.01%)+8,519k (+ 2.29%)379,686k379,811kp=0.005 n=6
Parse Time2.89s (± 0.60%)2.91s (± 0.96%)~2.86s2.93sp=0.192 n=6
Bind Time1.61s (± 1.69%)1.60s (± 1.46%)~1.57s1.63sp=0.333 n=6
Check Time16.51s (± 0.33%)16.54s (± 0.26%)~16.49s16.61sp=0.574 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time21.02s (± 0.26%)21.04s (± 0.25%)~20.99s21.12sp=0.628 n=6
vscode - node (v18.15.0, x64)
Errors33~~~p=1.000 n=6
Symbols3,336,0593,336,059~~~p=1.000 n=6
Types1,130,5071,130,507~~~p=1.000 n=6
Memory used3,394,122k (± 0.01%)3,466,746k (± 0.01%)+72,624k (+ 2.14%)3,466,227k3,466,964kp=0.005 n=6
Parse Time14.70s (± 0.34%)14.60s (± 0.53%)-0.10s (- 0.71%)14.51s14.69sp=0.030 n=6
Bind Time4.67s (± 0.62%)4.69s (± 0.61%)~4.66s4.74sp=0.514 n=6
Check Time93.33s (± 2.44%)93.32s (± 3.35%)~91.13s98.75sp=0.936 n=6
Emit Time29.88s (± 2.66%)30.10s (± 2.92%)~29.44s31.36sp=0.575 n=6
Total Time142.58s (± 2.06%)142.71s (± 2.81%)~139.84s149.43sp=1.000 n=6
webpack - node (v18.15.0, x64)
Errors22~~~p=1.000 n=6
Symbols310,441310,441~~~p=1.000 n=6
Types136,030136,030~~~p=1.000 n=6
Memory used465,164k (± 0.02%)475,453k (± 0.01%)+10,290k (+ 2.21%)475,362k475,517kp=0.005 n=6
Parse Time5.15s (± 0.56%)5.16s (± 0.94%)~5.13s5.26sp=0.418 n=6
Bind Time2.26s (± 0.99%)2.26s (± 1.23%)~2.24s2.31sp=0.934 n=6
Check Time25.48s (± 0.42%)25.67s (± 0.18%)+0.19s (+ 0.73%)25.62s25.74sp=0.005 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time32.89s (± 0.34%)33.09s (± 0.27%)+0.21s (+ 0.63%)33.00s33.25sp=0.013 n=6
xstate-main - node (v18.15.0, x64)
Errors55~~~p=1.000 n=6
Symbols567,041567,041~~~p=1.000 n=6
Types189,889189,889~~~p=1.000 n=6
Memory used497,780k (± 0.02%)519,637k (± 0.02%)🔻+21,857k (+ 4.39%)519,555k519,800kp=0.005 n=6
Parse Time3.36s (± 0.81%)3.37s (± 0.62%)~3.34s3.40sp=0.570 n=6
Bind Time1.21s (± 1.10%)1.20s (± 1.14%)~1.19s1.23sp=0.323 n=6
Check Time19.99s (± 0.10%)19.81s (± 0.38%)-0.18s (- 0.91%)19.67s19.88sp=0.004 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time24.56s (± 0.12%)24.38s (± 0.42%)-0.18s (- 0.75%)24.21s24.51sp=0.006 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@jakebailey
Copy link
Member

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Mar 31, 2025

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

CommandStatusResults
pack this✅ Started❌ Results

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, something went wrong when looking for the build artifact. (You can check the log here).

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

@ssalbdivad
Copy link

This is a set of instantiation benchmarks with results from 5.8.2 for related scenarios.

These can be run against the installed TS version by executing this with node or similar, or could just be used as a reference when constructing benchmarks for TS directly:

import{bench}from"@ark/attest"typemerge<base,props>=Omit<base,keyofprops&keyofbase>&propsdeclareconstmerge: <l,r>(l: l,r: r)=>merge<l,r>bench("functional(10)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})}).types([59386,"instantiations"])bench("functional(11)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})}).types([177537,"instantiations"])bench("functional(12)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})constk=merge(j,{l: 12})// should be linear relative to functional(10) and functional(11)}).types([531887,"instantiations"])bench("functional(12) with spread",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})constk=merge(j,{l: 12})return{ ...i, ...j, ...k}// should be similar to functional(12)}).types([797849,"instantiations"])typeType<t>={merge: <r>(r: r)=>Type<merge<t,r>>unwrapped: t}declareconsta: Type<{a: 1}>bench("chained(10)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})}).types([178394,"instantiations"])bench("chained(11)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})}).types([532898,"instantiations"])bench("chained(12)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})constl=k.merge({l: 12})// should be linear relative to chained(10) and chained(11)}).types([1596004,"instantiations"])bench("chained(12) with spread",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})constl=k.merge({l: 12})return{ ...i.unwrapped, ...j.unwrapped, ...k.unwrapped}// should be similar to chained(12)}).types([1684778,"instantiations"])

I would expect the instantiation scaling to not be purely linear for this sort of operation as the number of props is increasing, but it should be relatively close. None of these scenarios should result in the exponential scaling we see currently.

I'm also including the original end-to-end type benchmarks that identified this issue here. Once the minimal repo scaling has been addressed, we should sanity check that the scaling here is also fixed:

import{bench}from'@ark/attest';import{initTRPC}from'@trpc/server';import{type}from'arktype';import{z}from'zod';constt=initTRPC.create();// avoid pollution from one-time library setupbench.baseline(()=>{constrouter=t.router({baseline: t.procedure.input(z.object({baseline: z.string(),}),).query(({ input })=>`hello ${input.baseline}`),arkBaseline: t.procedure.input(type({baseline: 'string',}),).query(({ input })=>`hello ${input.baseline}`),});});constbase=z.object({a: z.string(),b: z.string(),c: z.string(),});bench('non-sequential Zod type',async()=>{constnonSequentialRouter=t.router({query1: t.procedure.input(base).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(base).mutation(({ input })=>`hello ${input.a}`),});// this is relatively cheap}).types([1659,'instantiations']);// even though sequential is totally equivalent to nonSequential, its// Zod representation is not reduced and still includes intermediate operationsconstsequential=base.partial().merge(base).pick({a: true,b: true,c: true}).omit({}).merge(base);constbase2=z.object({d: z.string(),e: z.string(),f: z.string(),});bench('isolated sequential zod',()=>{constsequential=base2.partial().merge(base2).pick({d: true,e: true,f: true}).omit({}).merge(base2);// this is expensive}).types([11420,'instantiations']);bench('sequential Zod type',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(sequential).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(sequential).mutation(({ input })=>`hello ${input.a}`),});// but it's in combination with trpc that these sequentially evaluated// Zod types get out of control. instead of incurring a 1-time evaluation// cost, it seems it can't be cached and the extra inference cost// is incurred multiple times (even worse with deepPartial)}).types([49173,'instantiations']);constarkBase=type({a: 'string',b: 'string',c: 'string',});bench('non-sequential arktype',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(arkBase).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(arkBase).mutation(({ input })=>`hello ${input.a}`),});// realtively cheap}).types([2961,'instantiations']);constarkBase2=type({d: 'string',e: 'string',f: 'string',});bench('isolated sequential arktype',()=>{arkBase2.partial().merge(arkBase2).pick('d','e','f').omit().merge(arkBase2);// these kind of operations are much cheaper in ArkType than Zod}).types([2336,'instantiations']);constarkSequential=arkBase.partial().merge(arkBase).pick('a','b','c').omit().merge(arkBase);bench('sequential arktype',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(arkSequential).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(arkSequential).mutation(({ input })=>`hello ${input.a}`),});// even though hovering arkSequential is identical to hovering arkBase,// TS still seems to do a lot of repeated work inferring it somehow (though less than Zod)}).types([17906,'instantiations']);

@AndaristAndaristforce-pushed the cache-mapper-instaniations branch from 688f5d5 to b0f5c70CompareApril 1, 2025 08:00
@Andarist
Copy link
ContributorAuthor

@ssalbdivad tested this in a trpc-based repo.

TS 5.8:

Types: 139629 Instantiations: 12133763 Memory used: 798162K Check time: 13.04s 

This branch (commit b0f5c70):

Types: 139629 Instantiations: 817105 Memory used: 819990K Check time: 6.00s 

@jakebailey
Copy link
Member

@typescript-bot test it
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Apr 1, 2025

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

CommandStatusResults
pack this✅ Started✅ Results
test top400✅ Started✅ Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@ssalbdivad
Copy link

@ahejlsberg This merge implementation was never meant to be the example of exponential instantiation scaling. It just happened to be easy to repro.

What concerns me is that whether or not individual instantiations are expensive, if there are O(3^N) of them, they will eventually dominate the check time of the entire type.

IMO given @Andarist's second PR has no clear downsides in terms of check time or memory consumption, it seems worthwhile to just eliminate the logic that could lead to this scaling altogether.

@Andarist
Copy link
ContributorAuthor

I'm still slightly concerned that caches might not always be invalidated. For example, if the nonFixingMapper associated with an inference context is combined with another mapper, then the cache of that combined mapper isn't cleared by clearCachedInferences, and it's not easy to see how it could be. This hypothetical issue doesn't show up in any of our tests, but I do think it could possibly happen.

This is a valid concern 👍 I'll see what I can do to address it

@ahejlsberg
Copy link
Member

This is a valid concern 👍 I'll see what I can do to address it

You might experiment with clearing all caches whenever a new inference is made. If that doesn't adversely affect performance I think that might be the best option. Also, as I mentioned earlier, you can remove some overhead by only caching results of union and intersection instantiations.

@jakebailey
Copy link
Member

@typescript-bot test it
@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 2, 2025

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

CommandStatusResults
pack this✅ Started✅ Results
test top400✅ Started✅ Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@typescript-bot
Copy link
Collaborator

typescript-bot commented May 2, 2025

Hey @jakebailey, I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{"devDependencies":{"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/165066/artifacts?artifactName=tgz&fileId=78AE6840F3C45DEE689460611669A8466E20BF2A28EC3B53C7765071FF04AB7A02&fileName=/typescript-5.9.0-insiders.20250502.tgz" } } 

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/[email protected]".;

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user tests with tsc comparing main and refs/pull/61505/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Timeout"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors3434~~~p=1.000 n=6
Symbols62,39062,390~~~p=1.000 n=6
Types50,39550,395~~~p=1.000 n=6
Memory used195,358k (± 0.94%)193,632k (± 0.75%)~192,921k196,597kp=0.230 n=6
Parse Time1.31s (± 1.08%)1.31s (± 0.79%)~1.29s1.32sp=0.738 n=6
Bind Time0.73s0.73s~~~p=1.000 n=6
Check Time9.79s (± 0.38%)9.78s (± 0.33%)~9.73s9.81sp=0.517 n=6
Emit Time2.73s (± 0.67%)2.73s (± 0.68%)~2.71s2.75sp=1.000 n=6
Total Time14.55s (± 0.23%)14.54s (± 0.38%)~14.47s14.59sp=1.000 n=6
angular-1 - node (v18.15.0, x64)
Errors5656~~~p=1.000 n=6
Symbols948,670948,670~~~p=1.000 n=6
Types410,947410,947~~~p=1.000 n=6
Memory used1,224,351k (± 0.00%)1,224,412k (± 0.00%)~1,224,360k1,224,514kp=0.128 n=6
Parse Time6.67s (± 0.45%)6.65s (± 0.77%)~6.57s6.70sp=0.628 n=6
Bind Time1.90s (± 0.40%)1.88s (± 0.58%)-0.02s (- 0.97%)1.87s1.89sp=0.015 n=6
Check Time31.90s (± 0.24%)32.10s (± 0.30%)+0.20s (+ 0.64%)31.96s32.23sp=0.010 n=6
Emit Time15.23s (± 0.90%)15.28s (± 0.36%)~15.21s15.37sp=0.573 n=6
Total Time55.71s (± 0.15%)55.92s (± 0.17%)+0.22s (+ 0.39%)55.82s56.07sp=0.005 n=6
mui-docs - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols2,361,6672,361,667~~~p=1.000 n=6
Types868,625868,625~~~p=1.000 n=6
Memory used2,317,105k (± 0.00%)2,318,136k (± 0.00%)+1,030k (+ 0.04%)2,318,073k2,318,171kp=0.005 n=6
Parse Time8.76s (± 0.21%)8.76s (± 0.29%)~8.73s8.80sp=0.465 n=6
Bind Time2.21s (± 0.78%)2.21s (± 1.01%)~2.19s2.25sp=0.934 n=6
Check Time69.90s (± 0.33%)69.85s (± 0.33%)~69.63s70.19sp=0.748 n=6
Emit Time0.30s (± 2.11%)0.30s (± 3.40%)~0.29s0.32sp=0.654 n=6
Total Time81.17s (± 0.30%)81.12s (± 0.28%)~80.89s81.46sp=0.630 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,228,0441,228,093+49 (+ 0.00%)~~p=0.001 n=6
Types267,260267,268+8 (+ 0.00%)~~p=0.001 n=6
Memory used2,973,179k (±10.03%)2,608,144k (±14.46%)~2,363,403k3,095,909kp=0.230 n=6
Parse Time6.69s (± 1.32%)6.62s (± 1.46%)~6.50s6.75sp=0.173 n=6
Bind Time2.18s (± 2.19%)2.17s (± 0.95%)~2.13s2.19sp=0.936 n=6
Check Time42.87s (± 0.43%)42.90s (± 0.46%)~42.70s43.21sp=0.936 n=6
Emit Time3.43s (± 3.59%)3.56s (± 5.02%)~3.34s3.85sp=0.230 n=6
Total Time55.17s (± 0.47%)55.27s (± 0.42%)~54.96s55.47sp=0.471 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,228,0441,228,093+49 (+ 0.00%)~~p=0.001 n=6
Types267,260267,268+8 (+ 0.00%)~~p=0.001 n=6
Memory used2,427,922k (± 0.03%)2,427,536k (± 0.01%)~2,427,071k2,428,053kp=0.298 n=6
Parse Time5.46s (± 0.88%)5.47s (± 0.74%)~5.40s5.51sp=0.573 n=6
Bind Time1.83s (± 1.02%)1.81s (± 1.03%)~1.79s1.84sp=0.331 n=6
Check Time35.40s (± 0.51%)35.52s (± 0.19%)~35.46s35.64sp=0.378 n=6
Emit Time3.04s (± 0.69%)3.06s (± 2.16%)~2.99s3.15sp=1.000 n=6
Total Time45.73s (± 0.47%)45.88s (± 0.29%)~45.75s46.08sp=0.298 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols263,411263,460+49 (+ 0.02%)~~p=0.001 n=6
Types107,096107,104+8 (+ 0.01%)~~p=0.001 n=6
Memory used441,780k (± 0.03%)441,847k (± 0.02%)~441,725k441,909kp=0.128 n=6
Parse Time3.55s (± 0.81%)3.53s (± 0.85%)~3.50s3.57sp=0.515 n=6
Bind Time1.31s (± 0.39%)1.31s (± 0.93%)~1.29s1.32sp=0.351 n=6
Check Time18.96s (± 0.42%)19.05s (± 0.33%)~18.98s19.12sp=0.064 n=6
Emit Time1.53s (± 1.64%)1.53s (± 0.72%)~1.52s1.55sp=0.805 n=6
Total Time25.35s (± 0.27%)25.42s (± 0.40%)~25.30s25.53sp=0.378 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors7171~~~p=1.000 n=6
Symbols225,981225,981~~~p=1.000 n=6
Types94,35694,356~~~p=1.000 n=6
Memory used371,253k (± 0.01%)371,323k (± 0.03%)~371,253k371,531kp=0.109 n=6
Parse Time2.89s (± 1.21%)2.89s (± 0.92%)~2.84s2.92sp=0.872 n=6
Bind Time1.58s (± 1.67%)1.59s (± 1.63%)~1.55s1.62sp=0.629 n=6
Check Time16.50s (± 0.27%)16.55s (± 0.26%)~16.49s16.59sp=0.147 n=6
Emit Time0.00s0.00s (±244.70%)~0.00s0.01sp=0.405 n=6
Total Time20.98s (± 0.30%)21.02s (± 0.17%)~20.98s21.07sp=0.148 n=6
vscode - node (v18.15.0, x64)
Errors44~~~p=1.000 n=6
Symbols3,391,6493,391,649~~~p=1.000 n=6
Types1,148,5941,148,594~~~p=1.000 n=6
Memory used3,443,382k (± 0.00%)3,443,190k (± 0.01%)~3,442,813k3,443,554kp=0.230 n=6
Parse Time14.69s (± 0.92%)14.70s (± 0.25%)~14.66s14.75sp=0.627 n=6
Bind Time5.01s (±11.96%)4.72s (± 0.39%)~4.70s4.75sp=0.569 n=6
Check Time92.54s (± 0.72%)94.92s (± 2.83%)~92.17s99.26sp=0.078 n=6
Emit Time29.79s (± 1.08%)30.06s (± 2.03%)~29.50s31.15sp=0.423 n=6
Total Time142.03s (± 0.68%)144.41s (± 1.93%)~141.31s148.14sp=0.128 n=6
webpack - node (v18.15.0, x64)
Errors22~~~p=1.000 n=6
Symbols317,345317,345~~~p=1.000 n=6
Types140,331140,331~~~p=1.000 n=6
Memory used472,396k (± 0.03%)472,299k (± 0.02%)~472,200k472,471kp=0.173 n=6
Parse Time5.16s (± 0.54%)5.17s (± 0.82%)~5.09s5.20sp=0.418 n=6
Bind Time2.25s (± 1.12%)2.25s (± 1.42%)~2.20s2.30sp=0.934 n=6
Check Time25.79s (± 0.56%)25.89s (± 0.34%)~25.78s26.00sp=0.173 n=6
Emit Time0.00s (±244.70%)0.00s (±244.70%)~0.00s0.01sp=1.000 n=6
Total Time33.20s (± 0.43%)33.32s (± 0.26%)~33.18s33.38sp=0.065 n=6
xstate-main - node (v18.15.0, x64)
Errors55~~~p=1.000 n=6
Symbols570,517570,517~~~p=1.000 n=6
Types191,417191,417~~~p=1.000 n=6
Memory used500,886k (± 0.03%)502,543k (± 0.01%)+1,657k (+ 0.33%)502,444k502,603kp=0.005 n=6
Parse Time4.32s (± 0.73%)4.31s (± 0.31%)~4.30s4.33sp=0.801 n=6
Bind Time1.53s (± 1.13%)1.53s (± 0.72%)~1.51s1.54sp=0.933 n=6
Check Time25.09s (± 1.86%)24.52s (± 0.27%)-0.57s (- 2.27%)24.44s24.59sp=0.005 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time30.93s (± 1.59%)30.36s (± 0.24%)-0.57s (- 1.84%)30.28s30.44sp=0.005 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

@Andarist
Copy link
ContributorAuthor

Andarist commented May 6, 2025

You might experiment with clearing all caches whenever a new inference is made. If that doesn't adversely affect performance I think that might be the best option.

I've followed this advice and the numbers are still pretty much the same as the ones I mentioned here. Instantiations went up a little bit but by such a negligible number (+341), it doesn't matter at all.

Also, as I mentioned earlier, you can remove some overhead by only caching results of union and intersection instantiations.

The current version doesn't come with any noticeable overhead, from what I can tell. It seems to me doing this just for union/intersections would only complicate the code right now. If you feel strongly about it though, I could certainly run an extra experiment like this.

Copy link
Member

@jakebaileyjakebailey left a comment

Choose a reason for hiding this comment

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

So, I feel like this must be good to go at this point. The code looks pretty simple, the benchmarks are good, and it should be straightforward to port into the Go codebase (eventually) as there aren't any goofy uses of maps / prototype modification anymore.

Are there any remaining concerns here?

@github-project-automationgithub-project-automationbot moved this from Not started to Needs merge in PR BacklogJun 5, 2025
@jakebaileyjakebailey merged commit 51dcd90 into microsoft:mainJun 6, 2025
32 checks passed
@github-project-automationgithub-project-automationbot moved this from Needs merge to Done in PR BacklogJun 6, 2025
@mikeduminy
Copy link

This was a game-changer PR for us. Thank you!

BeforeAfterImprovement
Instantiations704,556,46710,119,79398.56%
Check time395.48132.0966.60%
Total time425.86164.7761.31%

@jakebailey
Copy link
Member

What repo is that? Is it public?

@mikeduminy
Copy link

mikeduminy commented Jul 22, 2025

It's a private repo unfortunately. It is for the Klarna app (react-native, web, etc). But we'd be happy to test out anything internally and get back to you if want 😄

@jakebailey
Copy link
Member

jakebailey commented Jul 22, 2025

I want a public repo for our benchmarks, unfortunately

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

Labels

For Uncommitted BugPR for untriaged, rejected, closed or missing bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

9 participants

@Andarist@typescript-bot@jakebailey@ssalbdivad@ahejlsberg@fxdave@gavbaa@colinhacks@mikeduminy