TypeScript throws this error when type recursion or nesting exceeds internal depth limits (50-500 instantiations). This commonly happens with deeply nested generic types, recursive type definitions, or complex type compositions.
This error occurs when TypeScript's type checker encounters type definitions that are too deeply nested or potentially infinitely recursive. TypeScript imposes depth limits to prevent infinite loops and protect compiler performance. The compiler tracks how many times it needs to "instantiate" (resolve) a type during type checking. When this count exceeds the limit (increased from 50 to 500 in TypeScript 4.1+), the error is thrown. This is a safety mechanism to prevent stack overflows and keep the language server responsive. Common triggers include recursive conditional types, deeply composed generic types (like Redux selectors or Zod schemas), complex mapped types, and intricate type intersections. The error often appears after TypeScript version upgrades as the compiler becomes stricter or type definitions in dependencies become more sophisticated.
The error message usually points to the file and type causing the issue. Enable verbose compiler output:
// tsconfig.json
{
"compilerOptions": {
"extendedDiagnostics": true
}
}Look for the specific type or file mentioned in the error. If the error is vague, comment out recent changes or dependencies to isolate the source.
TypeScript infers types through potentially deep chains. Break these with explicit annotations:
// Before: TypeScript infers through multiple levels
const StyledButton = styled(Button);
// After: Explicit type stops deep inference
const StyledButton = styled(Button) as typeof Button;
// For function compositions
const selector = createSelector(
inputSelector,
(state) => state.data
) as (state: RootState) => DataType;This tells TypeScript to use the simpler asserted type rather than computing a complex inferred type.
Add a depth parameter to prevent infinite recursion:
// Before: Potentially infinite recursion
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// After: Depth-limited version
type DeepPartial<T, D extends number = 5> = D extends 0
? T
: {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P], Prev[D]>
: T[P];
};
type Prev = [never, 0, 1, 2, 3, 4, 5, ...0[]];The depth limit prevents TypeScript from recursing too deeply.
Wrapping recursive references in object properties or arrays defers instantiation:
// Before: Immediate instantiation
type Tree<T> = T | Tree<T>[];
// After: Deferred instantiation
type Tree<T> = T | { children: Tree<T>[] };This works because TypeScript doesn't immediately instantiate types inside object properties, reducing the instantiation depth.
If the error comes from node_modules type definitions:
// tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true
}
}This skips type checking of declaration files (.d.ts) in node_modules. This is a workaround, not a fix, but can unblock builds while waiting for library updates.
Warning: This may hide legitimate type errors in dependencies.
TypeScript 4.5+ includes tail-recursion optimization for conditional types:
npm install typescript@latest --save-devAlso update type-heavy dependencies:
npm update @types/react @reduxjs/toolkit zodNewer versions often include type optimizations that reduce instantiation depth. Check release notes for "type instantiation" or "recursion depth" fixes.
Tail Recursion Optimization: TypeScript 4.5+ performs tail-recursion elimination on conditional types. If one branch of a conditional type is simply another conditional type, TypeScript avoids intermediate instantiations:
type InfiniteLoop<T> = T extends string ? InfiniteLoop<T> : never;
// TypeScript 4.5+ optimizes this tail-recursive patternIntersection Type Counter Reset: Using intersection types can reset TypeScript's internal counter, extending limits:
type DeepType<T> = T & { /* properties */ };
// The intersection resets the instantiation counterCompiler Internals: The limit exists because resolution of recursive types causes deeply nested invocations in the instantiateType function, risking stack overflows. The limit was 50 in TypeScript 4.0, increased to 500 in 4.1 via PR #45025.
Library-Specific Issues: Some libraries are particularly prone to this error:
- Redux Toolkit: Deep selector composition with createSelector
- Zod: Recursive schema definitions with .lazy()
- i18next: Complex translation key type generation
- Kysely: Type-safe query builders with deep joins
For these, consult library-specific documentation (e.g., Kysely's "Dealing with excessively deep types" guide).
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript