This TypeScript compiler error occurs when the type system encounters a union type with excessive combinations that exceed its internal complexity limits. The fix typically involves simplifying type structures, adding explicit type annotations, or refactoring complex type operations.
TypeScript has internal limits on how complex union types can become before they become unmanageable. When the compiler tries to infer or compute a union type with too many possible combinations—often through recursive type transformations, deep property spreading, or extensive conditional types—it hits this limit and throws error TS2590. This error commonly appears in three scenarios: 1. **Complex component libraries**: UI frameworks like Chakra UI or Material-UI with deeply nested prop types that generate thousands of union combinations 2. **Recursive type transformations**: Types that recursively map over large object structures, multiplying type complexity exponentially 3. **Large discriminated unions**: Combining multiple large union types together, creating a combinatorial explosion The error is not a bug in your code—it's TypeScript's way of preventing compilation from taking excessive time or memory. The type system has pragmatic limits to keep performance reasonable, and this error signals you've exceeded them.
Instead of relying on TypeScript's type inference, explicitly define the type for the problematic expression:
// BEFORE: TypeScript tries to infer complex union
const config = {
...defaultConfig,
...themeConfig,
...variantConfig,
};
// AFTER: Explicitly type the result
interface Config {
theme: string;
variant: string;
// ... other properties
}
const config: Config = {
...defaultConfig,
...themeConfig,
...variantConfig,
};For component props:
// BEFORE: Complex inference
<Button {...complexProps} />
// AFTER: Type the props explicitly
const buttonProps: ButtonProps = { ...complexProps };
<Button {...buttonProps} />Explicit types help TypeScript avoid computing massive union combinations.
Refactor complex types into smaller, composable pieces:
// BEFORE: One massive type
type ComplexConfig = VariantA | VariantB | VariantC | ... // 100 variants
// AFTER: Group into categories
type SizeVariants = "sm" | "md" | "lg";
type ColorVariants = "primary" | "secondary" | "tertiary";
type ComponentConfig = {
size: SizeVariants;
color: ColorVariants;
};For component props:
// BEFORE: Spreading many complex objects
const allProps = { ...propsA, ...propsB, ...propsC, ...propsD };
// AFTER: Merge incrementally with explicit types
const intermediateProps: IntermediateType = { ...propsA, ...propsB };
const finalProps: FinalType = { ...intermediateProps, ...propsC };TypeScript's incremental build cache can sometimes become corrupted and reference stale complex types:
# Delete the build cache file
rm tsconfig.tsbuildinfo
# Also clear node_modules cache if using TypeScript from dependencies
rm -rf node_modules/.cache
# Rebuild from scratch
npm run build
# or
npx tsc --build --clean
npx tsc --buildIn your IDE (VS Code):
1. Open command palette (Ctrl+Shift+P / Cmd+Shift+P)
2. Run "TypeScript: Restart TS Server"
3. Check if the error persists
This often resolves the error when it's caused by stale incremental build state rather than actual type complexity.
When you know the actual type but TypeScript can't infer it, use type assertions:
// BEFORE: Complex union inference
const result = complexFunction(args);
// AFTER: Assert the specific type
const result = complexFunction(args) as SpecificType;For truly complex scenarios where you need to escape the type system temporarily:
// Use any as an escape hatch (use sparingly!)
const intermediate = complexExpression as any;
const result = intermediate as ExpectedType;For union types, narrow them explicitly:
// Instead of letting TypeScript compute all combinations
type Result = ComponentVariantA | ComponentVariantB;
if (isVariantA(props)) {
// TypeScript knows it's ComponentVariantA here
return <ComponentA {...props} />;
} else {
return <ComponentB {...props} />;
}Different TypeScript versions have different complexity limits. If you recently upgraded:
# Check your current version
npx tsc --version
# Try a different version
npm install --save-dev [email protected]
# or
npm install --save-dev [email protected]In some cases, newer versions (5.0+) introduced stricter limits. Check your project's changelog and TypeScript release notes.
Ensure your IDE uses the workspace TypeScript version:
1. VS Code: Open command palette
2. Run "TypeScript: Select TypeScript Version"
3. Choose "Use Workspace Version"
Common working versions for libraries with complex types:
- TypeScript 4.9.x (stable, less strict)
- TypeScript 5.0.4 (balanced)
- TypeScript 5.1+ (stricter limits)
While there's no direct setting to increase union complexity limits, some compiler options can help:
{
"compilerOptions": {
// Reduce type-checking strictness temporarily to isolate the issue
"skipLibCheck": true,
// Disable incremental builds if cache is problematic
"incremental": false,
// For libraries, sometimes this helps
"moduleResolution": "node",
// Ensure proper type resolution
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}Note: skipLibCheck: true can mask the problem if it's in node_modules type definitions, but doesn't fix your own code.
If the error is from a third-party library:
{
"compilerOptions": {
"skipLibCheck": true // Skip type checking in node_modules
}
}### Understanding TypeScript's Union Complexity Limits
TypeScript uses a heuristic-based approach to limit union type complexity. There's no publicly documented hard number, but the limit is roughly around:
- ~100,000 type instantiations per expression
- ~50 levels of nested unions in conditional types
- Exponential growth from recursive mapped types
The limit exists to prevent:
- Compilation hanging or taking minutes/hours
- Excessive memory usage (>8GB for a single type check)
- IDE responsiveness degradation
### Common Culprits: Component Libraries
Libraries like Chakra UI, Material-UI, and styled-components often trigger this error because they:
1. Generate exhaustive prop combinations (variants × sizes × colors × states)
2. Use discriminated unions extensively for type safety
3. Spread props across multiple abstraction layers
Example problematic pattern:
// This creates size × color × variant combinations
type ButtonProps =
| { size: "sm"; color: "primary"; variant: "solid"; /* 20 more props */ }
| { size: "sm"; color: "primary"; variant: "outline"; /* ... */ }
| /* 500+ more union members */### Workarounds for Library Types
If the error comes from a library you can't modify:
// Create a simplified interface that covers your use case
interface SimplifiedButtonProps {
size?: "sm" | "md" | "lg";
colorScheme?: string;
variant?: string;
children: React.ReactNode;
}
// Use it instead of the library's complex type
<Button {...(props as SimplifiedButtonProps)} />### TypeScript GitHub Issues
This is a known limitation. Relevant TypeScript issues:
- [#45149](https://github.com/microsoft/TypeScript/issues/45149) - Union complexity in TS 4.5+
- [#33130](https://github.com/microsoft/TypeScript/issues/33130) - Original discussion
- [#58301](https://github.com/microsoft/TypeScript/issues/58301) - Cache corruption causing false positives
### Preventing Future Issues
1. Avoid excessive type spreading: Limit chained { ...a, ...b, ...c } operations
2. Use interfaces over type unions when possible (interfaces merge, unions multiply)
3. Profile your types: Use tsc --extendedDiagnostics to see which files are slowest
4. Keep utility types shallow: Deeply nested Pick<Omit<Partial<...>>> chains are problematic
### When to Use @ts-ignore
As a last resort:
// @ts-ignore TS2590: Expression produces union type too complex
const result = veryComplexExpression;Only use this if:
1. You've verified the runtime code works correctly
2. The error is from a third-party library you can't modify
3. You've tried all other solutions
4. You've documented why the ignore is necessary
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