You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tsserver and intellisense hang indefinitely on "loading intellisense status" and "loading..." when using assertion function and complex conditional type #51188
tsserver typescript langauge server loading intellisense status indefinite error forever asserts assertion function vscode conditional type
🕗 Version & Regression Information
This changed between versions 4.7.4 and 4.8.0-beta and happens in all versions up to 4.9.0-dev.20221015
⏯ Playground Link
Unfortunately, painstakingly copy-pasting all the types from the various packages into the playground doesn't allow me to reproduce the issue. So instead, I've created a minimal example repository that demonstrates the issue: https://github.com/Xunnamius/mre-typescript-issue-51188
💻 Code
import{visit,SKIP,typeVisitor}from'unist-util-visit';importtype{ContentasMdastContent}from'mdast';functionassertNonNullable<T>(value: T,err: string): asserts value is NonNullable<T>{if(value===null){thrownewError(err);}}exportfunctionvisitAndReveal<TreeextendsMdastContent>(tree: Tree,visitor?: Visitor,reverse?: boolean){visit(tree,'hidden',(node,index,parent)=>{// UNCOMMENTING THIS WILL KILL INTELLISENSE// assertNonNullable(// index,// 'error while revealing hidden node: node index is missing'// );// UNCOMMENTING THIS WILL KILL INTELLISENSE// assertNonNullable(// parent,// 'error while revealing hidden node: node parent is missing'//);returnvisitor?.(node,index,parent)??[SKIP,index];},reverse);}
🙁 Actual behavior
Tsserver and intellisense in vscode (latest version) hang when using a TypeScript version >=4.8.0-beta. See video example below.
🙂 Expected behavior
Tsserver/intellisense does not hang in latest versions of vscode and TypeScript.
Stumbled across this one and took a quick look. The core issue is that the types in the repo are very expensive to compute, as is evident by the painful slowness of intellisense in the example. For example, checking the visitAndReveal function in the example causes ~5.8M type instantiations in 4.7 when compiled with tsc. This shrinks to ~1.2M type instantiations in 4.8 and later. Those numbers are big warning flags that the types are too complex.
That said, we obviously shouldn't hang the language service and I'm not sure why that happens. The example compiles with tsc with or without the commented out section, and I would expect the same from the language service.
For example, checking the visitAndReveal function in the example causes ~5.8M type instantiations in 4.7 when compiled with tsc. This shrinks to ~1.2M type instantiations in 4.8 and later. Those numbers are big warning flags that the types are too complex.
Thanks for taking a look! I had a suspicion complexity was a problem after some other small issues with the types coming from the unist-util-visit package. For example, sometimes TS would give me "Type instantiation is excessively deep and possibly infinite" seemingly at random, then I'd restart the language server, and it would return the expected type.
For example, sometimes TS would give me "Type instantiation is excessively deep and possibly infinite" seemingly at random
That error is caused by our type instantiation governor when a single source element gives rise to >5M type instantiations. Any code that triggers this is definitely highly suspect. It looks like you were barely managing to squeeze under the limit in 4.7, so I would strongly recommend simplifying the types. In particular, the InclusiveDescendant<...> type appears expensive. I think it is trying to reduce the set of possible descendant types. I don't know how important that added precision is, but it clearly comes at a very high cost.
I've been looking at this one, and if you uncomment just the second commented-out snippet in the linked example repo, tsc runs out of memory and crashes, so this isn't just an issue with tsserver/intellisense.
It seems like this code is generating huge unions of huge intersections of huge unions of... and so on, and post-#49119 we can't handle it anymore; it spends all its time traversing these in relation checking and doing so turns into a huge combinatorial explosion trying to see if two of these huge types are related.
So, just for reference, with TS 4.7, the example in my previous comment (the repro with the second block uncommented) takes ~2.18s to compile.
One thing I noticed while investigating this is that the intersections produced by the code here are big, but appear to be repetitive and only differ in ordering. In #52891 I am playing around with sorting intersections.
Just sorting the intersections themselves (nothing else) allows the example to compile in about 40 seconds, which is still Not Good ™️ but better than OOMing.
However, Daniel Rosenwasser (@DanielRosenwasser) pointed out that there were a few fast paths which now would work for intersections so now #52891fully mitigates the performance problem and then some, bringing the compile time down to 1.56s, which is 40% faster!!
I'm going to try and finalize this change, e.g. add origin types to intersections to fix the display issues, but given the nature of this change, it's not likely that this can go into TS 5.0 given we're nearly at RC.
My q above still stands btw: if anyone knows of a better way to do this (#51188 (comment)), I'd gladly use that.
But I don't think the stuff I came up with is very weird, so other TS users might run into similar problems!
Unfortunately, I completely forgot that intersection ordering determines overload order, and therefore we can't sort intersections at all, so I'll have to go back to the drawing board on this.
I was looking at this again and was surprised to find that main no longer OOMs. It turns out that #53012 (which was backported to TS 5.0 just in time for the full release) stops the OOM on this code example. I can now compile the example in ~12s, which is still a lot slower than TS 4.7's ~2s, but at least it completes!
Activity
jakebailey commentedon Oct 21, 2022
This seems to bisect to #49119.
ahejlsberg commentedon Oct 27, 2022
Stumbled across this one and took a quick look. The core issue is that the types in the repo are very expensive to compute, as is evident by the painful slowness of intellisense in the example. For example, checking the
visitAndRevealfunction in the example causes ~5.8M type instantiations in 4.7 when compiled withtsc. This shrinks to ~1.2M type instantiations in 4.8 and later. Those numbers are big warning flags that the types are too complex.That said, we obviously shouldn't hang the language service and I'm not sure why that happens. The example compiles with
tscwith or without the commented out section, and I would expect the same from the language service.jakebailey commentedon Oct 27, 2022
Very odd that we hang in 4.8+ when those versions have the fewer instantiations; I would have assumed the opposite.
Xunnamius commentedon Oct 27, 2022
Thanks for taking a look! I had a suspicion complexity was a problem after some other small issues with the types coming from the unist-util-visit package. For example, sometimes TS would give me "Type instantiation is excessively deep and possibly infinite" seemingly at random, then I'd restart the language server, and it would return the expected type.
cc unified team: Titus (@wooorm)
ahejlsberg commentedon Oct 27, 2022
That error is caused by our type instantiation governor when a single source element gives rise to >5M type instantiations. Any code that triggers this is definitely highly suspect. It looks like you were barely managing to squeeze under the limit in 4.7, so I would strongly recommend simplifying the types. In particular, the
InclusiveDescendant<...>type appears expensive. I think it is trying to reduce the set of possible descendant types. I don't know how important that added precision is, but it clearly comes at a very high cost.wooorm commentedon Oct 28, 2022
A TS wizard can probably improve
InclusiveDescendant. It’s quite useful though, I’d rather not yank it out if possible.jakebailey commentedon Feb 15, 2023
I've been looking at this one, and if you uncomment just the second commented-out snippet in the linked example repo,
tscruns out of memory and crashes, so this isn't just an issue with tsserver/intellisense.It seems like this code is generating huge unions of huge intersections of huge unions of... and so on, and post-#49119 we can't handle it anymore; it spends all its time traversing these in relation checking and doing so turns into a huge combinatorial explosion trying to see if two of these huge types are related.
jakebailey commentedon Feb 21, 2023
So, just for reference, with TS 4.7, the example in my previous comment (the repro with the second block uncommented) takes ~2.18s to compile.
One thing I noticed while investigating this is that the intersections produced by the code here are big, but appear to be repetitive and only differ in ordering. In #52891 I am playing around with sorting intersections.
Just sorting the intersections themselves (nothing else) allows the example to compile in about 40 seconds, which is still Not Good ™️ but better than OOMing.
However, Daniel Rosenwasser (@DanielRosenwasser) pointed out that there were a few fast paths which now would work for intersections so now #52891 fully mitigates the performance problem and then some, bringing the compile time down to 1.56s, which is 40% faster!!I'm going to try and finalize this change, e.g. add origin types to intersections to fix the display issues, but given the nature of this change, it's not likely that this can go into TS 5.0 given we're nearly at RC.
jakebailey commentedon Feb 21, 2023
Ugh.... nevermind, I screwed up; I had commented out the slow code in my example so I was testing the repro without modification! Very embarrassing.
There's still a benefit, which is still good compared to OOMing, but, not what I was hoping. Looking for more now.
jakebailey commentedon Feb 21, 2023
That being said... it's still 40% for the non-bad case, which is promising in and of itself.
wooorm commentedon Feb 21, 2023
My q above still stands btw: if anyone knows of a better way to do this (#51188 (comment)), I'd gladly use that.
But I don't think the stuff I came up with is very weird, so other TS users might run into similar problems!
jakebailey commentedon Feb 21, 2023
Unfortunately, I completely forgot that intersection ordering determines overload order, and therefore we can't sort intersections at all, so I'll have to go back to the drawing board on this.
jakebailey commentedon Mar 18, 2023
I was looking at this again and was surprised to find that main no longer OOMs. It turns out that #53012 (which was backported to TS 5.0 just in time for the full release) stops the OOM on this code example. I can now compile the example in ~12s, which is still a lot slower than TS 4.7's ~2s, but at least it completes!
RyanCavanaugh commentedon Dec 3, 2025
Closing language service bugs related to the 6.0 implementation. For more information, see #62827