How to fix Expression produces a union type that is too complex to represent in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 10, 2025· 137 views

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.

Quick fix

Fixes TS2590

// 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,
};
  1. Add explicit type annotations
  2. Simplify type structures by breaking them down
  3. Clear TypeScript cache and rebuild
  4. Use type assertions or narrowing
typescript — error
// 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,
};
Expression produces a union type that is too complex to represent

What this error means

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.

Typical symptoms

  • Compilation fails with error TS2590 on a specific expression or type
  • Error occurs in code using third-party component libraries (Chakra UI, MUI, styled-components)
  • IDE shows red squiggly lines on otherwise valid-looking code
  • Error appears after upgrading TypeScript versions (newer versions may have stricter limits)
  • IntelliSense becomes slow or unresponsive around the error location
  • The error references a type with many conditional branches or union members

Common causes

  1. Component library props with extensive variant combinations creating thousands of unions
  2. Spreading large numbers of complex types together in a single expression
  3. Recursive utility types that expand exponentially (deep Pick, Omit, Partial chains)
  4. Large enums used in React hook dependency arrays or conditional types
  5. Combining multiple discriminated unions with many members each
  6. TypeScript version upgrade tightening internal complexity limits
  7. Cache corruption in incremental builds (tsconfig.tsbuildinfo) referencing stale complex types
  8. Deeply nested mapped types over large object structures

How to fix "Expression produces a union type that is too complex to represent"

1Add explicit type annotations

Instead of relying on TypeScript's type inference, explicitly define the type for the problematic expression:

typescript
// 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:

typescript
// 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.

2Simplify type structures by breaking them down

Refactor complex types into smaller, composable pieces:

typescript
// 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:

typescript
// 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 };
3Clear TypeScript cache and rebuild

TypeScript's incremental build cache can sometimes become corrupted and reference stale complex types:

bash
# 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 --build

In 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.

4Use type assertions or narrowing

When you know the actual type but TypeScript can't infer it, use type assertions:

typescript
// 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:

typescript
// Use any as an escape hatch (use sparingly!)
const intermediate = complexExpression as any;
const result = intermediate as ExpectedType;

For union types, narrow them explicitly:

typescript
// 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} />;
}
5Update or downgrade TypeScript version

Different TypeScript versions have different complexity limits. If you recently upgraded:

bash
# Check your current version
npx tsc --version

# Try a different version
npm install --save-dev typescript@5.0.4
# or
npm install --save-dev typescript@4.9.5

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)

6Adjust tsconfig.json compiler options

While there's no direct setting to increase union complexity limits, some compiler options can help:

json
{
  "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:

json
{
  "compilerOptions": {
    "skipLibCheck": true  // Skip type checking in node_modules
  }
}

Advanced notes

### 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:

typescript
// 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:

typescript
// 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:

typescript
// @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

Frequently asked questions

What does "TS2590" mean?
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.
How do I fix "TS2590"?
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.
What causes "TS2590"?
Component library props with extensive variant combinations creating thousands of unions Spreading large numbers of complex types together in a single expression Recursive utility types that expand exponentially (deep Pick, Omit, Partial chains)
Is the "TS2590" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading