The editor takes 30s to show errors in one package. What do these numbers say?
Engineers report that the editor needs tens of seconds to surface errors in one package, although the repository is not unusually large. Running tsc --noEmit --extendedDiagnostics on that package prints the numbers below.
Constraints: the machine is not memory-starved, the disk is fast, and the file count has not moved in months. Answers of the form "buy a faster machine" are out of scope.
Files: 1842
Lines of TypeScript: 214930
Nodes: 912447
Symbols: 498112
Types: 1284037
Instantiations: 48120665
Memory used: 3184219K
Assignability cache size: 922841
Parse time: 1.42s
Bind time: 0.71s
Check time: 96.30s
Emit time: 0.00s
Total time: 98.43s
Diagnose the cause.
Parse and bind take two seconds; check takes ninety-six, and 48 million instantiations across 1842 files is wildly out of proportion. That is a type-level blow-up, not file count and not disk. Take a --generateTrace and find the generic.
- ✗Blaming file count or disk when parse and bind time are already near zero
- ✗Reading the large memory figure as the cause rather than a consequence of instantiation
- ✗Guessing at the culprit generic instead of taking a trace and looking
- →What does the assignability-cache size tell you when read next to the instantiation count?
- →Which type constructs most often produce an instantiation blow-up like this one?
Read the profile top to bottom. Parse plus bind is 2.1 seconds across 1842 files: I/O and source volume are simply not the problem. All of the time sits in checking. And 48 million instantiations across 1842 files is roughly 26 000 instantiations per file — the types are avalanching.
The trace names the culprit. It is usually a generic that gets re-instantiated at every use because the compiler has nowhere to cache the result:
// Every reference to DeepPartial<T> expands the whole tree again
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };
function patch<T extends object>(base: T, delta: DeepPartial<T>): T { /* ... */ }
The cure is to narrow the type-level work, not to buy a faster CPU: replace the recursive mapped type with an explicit interface at the boundary, annotate the places where inference re-instantiates the generic, and split the package so the expensive type tree lives in one project. Re-run and compare the same Instantiations counter — an order-of-magnitude drop is the proof.