Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tsserver and intellisense hang indefinitely on "loading intellisense status" and "loading..." when using assertion function and complex conditional type #51188

Open
Xunnamius opened this issue Oct 15, 2022 · 13 comments
Assignees
Labels
Bug A bug in TypeScript Rescheduled This issue was previously scheduled to an earlier milestone

Comments

@Xunnamius
Copy link

Xunnamius commented Oct 15, 2022

Bug Report

🔎 Search Terms

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, type Visitor } from 'unist-util-visit';

import type { Content as MdastContent } from 'mdast';

function assertNonNullable<T>(
  value: T,
  err: string
): asserts value is NonNullable<T> {
  if (value === null) {
    throw new Error(err);
  }
}

export function visitAndReveal<Tree extends MdastContent>(
  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'
      //);

      return visitor?.(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.


Untitled.mp4
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Oct 17, 2022
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 5.0.0 milestone Oct 17, 2022
@jakebailey
Copy link
Member

This seems to bisect to #49119.

@ahejlsberg
Copy link
Member

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.

@jakebailey
Copy link
Member

Very odd that we hang in 4.8+ when those versions have the fewer instantiations; I would have assumed the opposite.

@Xunnamius
Copy link
Author

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.

cc unified team: @wooorm

@ahejlsberg
Copy link
Member

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.

@wooorm
Copy link

wooorm commented 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
Copy link
Member

jakebailey commented 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, 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.

@jakebailey
Copy link
Member

jakebailey commented 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, @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
Copy link
Member

jakebailey commented 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
Copy link
Member

That being said... it's still 40% for the non-bad case, which is promising in and of itself.

@wooorm
Copy link

wooorm commented 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
Copy link
Member

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
Copy link
Member

jakebailey commented 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Rescheduled This issue was previously scheduled to an earlier milestone
Projects
None yet
Development

No branches or pull requests

5 participants