Piszesz typ, który przechodzi przez zagnieżdżone obiekty, budując ścieżki rozdzielone kropkami do autouzupełniania. Na małym obiekcie testowym działa to doskonale. Jednak gdy kierujesz go na rzeczywisty ładunek (payload) API, edytor zamarza. W końcu TypeScript wyrzuca błąd TS2589: Type instantiation is excessively deep and possibly infinite.
Ten komunikat nie oznacza, że Twój kod zawiera nieskończoną pętlę w tradycyjnym sensie. Oznacza on, że kompilator się poddał. Typ, którego obliczenie zleciłeś, był albo faktycznie nieograniczony,
Wrap type parameters in tuples to block distribution. A naked type parameter in a conditional, like
T extends Foo ? Bar : Baz, distributes the check across every member whenTis a union. If that union has fifty members, TypeScript performs fifty separate instantiations. Writing[T] extends [Foo] ? Bar : Bazevaluates the conditional once against the whole union. Use this whenever you do not actually need the type to map over each union member individually.Shrink your inputs while debugging. When TS2589 appears, swap your production object type for a tiny stub with two properties and one level of nesting. If the error vanishes, you have confirmed that depth or cardinality is the issue, not a syntax mistake. This saves you from rewriting logic that was actually fine structurally.
Soften public-facing API types. Internally, you might need surgical precision. Externally, perfection sometimes costs more than it pays. If a slightly wider autocomplete type prevents a two-second lag in the editor, the trade is usually worth it. You can pair the looser type with a runtime validator to catch bad paths in testing.
Why TypeScript Enforces This Boundary
TypeScript cannot solve the halting problem. It does not know whether your recursive type will eventually terminate or spiral forever. Rather than risk an infinite loop inside the compiler, it enforces a conservative cutoff. Sometimes that cutoff catches a type that would have finished, given enough time. TS2589 is the compiler admitting it would rather be safe than sorry.
Respecting that limit is part of writing production-grade types. A type definition is code that runs in the compiler, and expensive code has real consequences. Slow autocomplete hurts developer velocity just as much as slow runtime code hurts user experience.
The Real Takeaway
TS2589 is not a signal that you are a bad type-system programmer. It is a signal that your type is doing too much work at once. Cap your recursion, validate lazily, and guard against unnecessary distribution. The goal of advanced types is not to prove every possible truth at compile time; it is to give your team fast, reliable tooling. A type that compiles in milliseconds and covers ninety-five percent of cases is far more valuable than one that is theoretically perfect but crashes the language server.
