Open shigma opened 3 years ago
@ahejlsberg or @weswigham interested in looking at this?
I have looked at this and have a pretty good grasp of what's up at this point. First: You "only" have 239 top-level events... but each of those events may have multiple possible payload shapes. So the handler arrow function signature's contextual type looks like (payload: A1) => void | ... | (payload: A239) => void
, where A1
through A239
are themselves unions of 1 or more object types. When we have a contextual union type like this, we attempt to combine those disparate signatures into a single contextual signature. To do that, we intersect all those parameter types. That's where the limiter comes into play - our simple analysis sees that based on the union+nested intersection count, there's >200,000 possible combinations in that calculated parameter type intersection. What that complexity analysis doesn't currently know, however, is that almost all of those 200,000+ possible combinations will reduce away to never
due to conflicting discriminant properties (their type
field).
So there's two things I've come up with that we can do to improve here, and I have implementations for both, I just don't know if we wanna do both, and, if so, together or separately.
on
has no explicit arguments. That means we shouldn't need to actually look at the argument types of the composite signature (since there's no argument position we actually need to contextually type). We can make it so we don't eagerly pull on and normalize the constituent types with a pretty straightforward usage of and modification to our existing SymbolFlags.DeferredType
machinery for deferring union/intersection property type normalization (so it also covers union/intersection parameters). That will help the example as written, but if you rewrite it so it actually has an argument, eg, on('message', arg => {})
, you're right back to an expression complexity error. Still, that allows the example given to compile in ~2s in my machine without a complexity error.on
is called (because it's part of a constraint calculation on the signature of on
), so the overhead is relatively fixed... but actually reducing the type just that one time adds 52s to what is otherwise a 2s compilation on my really beefy machine. This might be able to be reduced with some investigation in faster recognition/application of reduction. Anders' recent work may be relevant here.Hello, are there any plans to handle this particular issue? I am still seeing this error on typescript 4.5.5. Or Can you please provide a workaround like a tsconfig.json settings to disable/set higher limit for the too complex type error?
Bug Report
I wrote a framework (namely koishi) which uses a fully-typed event emitter implemented by itself. However when I create more events (totally 239), the type check just broke with an error ts2590 (Expression produces a union type that is too complex to represent). It seems that the error was thrown here:
I know 239 events is a lot, but I still cannot figure out how the 239 events became "100000 type checks".
Can you help prevent this error either by optimize type check performance or by improve my code? Thanks.
🔎 Search Terms
union type, interface, event emitter, ts2590
🕗 Version & Regression Information
I tried with v4.1.3, v4.1.5 & v4.2.1-rc. They all failed.
⏯ Playground Link
This playground will not work because it imports a dependency
@octokit/webhooks-definitions
(it's just a simple collection of github webhooks type definitions, but it is too big (over 5000 lines) to be included as I thought).Playground link with a dependency
And the dependency code can be found here: https://unpkg.com/@octokit/webhooks-definitions@3.60.0/schema.d.ts
💻 Code
🙁 Actual behavior
TS2590 error: Expression produces a union type that is too complex to represent.
🙂 Expected behavior
No compiler error