TS2590 in styled-components' own .d.ts since 6.5.0 when HTMLAttributes has a template-literal index signature
Since 6.5.0, styled-components' own declaration files fail to type-check with TS2590 when React.HTMLAttributes carries a template-literal index signature — the common augmentation used to allow arbitrary data-* attributes.
Because the errors are located inside node_modules/styled-components/dist/*.d.ts, any project that does not set skipLibCheck: true cannot compile at all.
node_modules/styled-components/dist/constructors/styled.d.ts(13,75): error TS2590: Expression produces a union type that is too complex to represent.
node_modules/styled-components/dist/types.d.ts(169,13): error TS2590: Expression produces a union type that is too complex to represent.Minimal reproduction
Three files, no styled component ever declared — a bare import is enough, so this is purely declaration-level.
src/react-aug.d.ts
import "react";
declare module "react" {
interface HTMLAttributes<T> {
[dataAttr: `data-${string}`]: string | undefined;
}
}src/index.tsx
import "styled-components";tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["DOM", "ES2025"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2025",
"strict": true,
"noEmit": true,
"skipLibCheck": false
},
"include": ["src"]
}npm i [email protected] [email protected] [email protected] \
@types/[email protected] @types/[email protected] @types/[email protected] \
@types/[email protected] [email protected]
npx tsc -p tsconfig.jsonVersions affected
Same repro, only the styled-components version changed:
| version | TS2590 count |
|---|---|
| 6.4.1 | 0 |
| 6.4.2 | 0 |
| 6.4.3 | 0 |
| 6.4.4 | 0 |
| 6.5.0 | 2 |
| 6.5.1 | 2 |
| 6.5.2 | 2 |
Introduced in 6.5.0 and still present in 6.5.2.
Environment: TypeScript 6.0.3, React 19.2.6, @types/react 19.2.17, macOS.
Possible cause
6.5.0 moved the inline-style CSS-custom-property widening into TargetProps, so styled.d.ts:13 now applies it eagerly for every tag:
declare const styled: typeof baseStyled & { [E in SupportedHTMLElements]: StyledInstance<"web", E, TargetProps<"web", E>>; };IntrinsicProps<T> resolves to WithCSSVars<React.JSX.IntrinsicElements[T]>, whose Omit<P, 'style'> is Pick<P, Exclude<keyof P, 'style'>>. When keyof P includes a template-literal key, that Pick appears to be what pushes the union past the complexity limit once it is instantiated across all ~170 elements.
The trigger is specifically a template-literal key, not an index signature in general. Swapping the augmentation to a plain string index signature is fine on every 6.5.x:
augmentation on HTMLAttributes |
6.5.0 | 6.5.1 | 6.5.2 |
|---|---|---|---|
[dataAttr: `data-${string}`]: string | undefined |
2 | 2 | 2 |
[dataAttr: string]: unknown |
0 | 0 | 0 |
(To be clear, the plain-string form was never broken — that row is a contrast, not a regression that 6.5.2 fixed.)
That contrast may still be useful, because the guard added to OverrideStyle in 6.5.2 keys off string exactly:
type OverrideStyle<P extends BaseObject> = P extends unknown ? string extends keyof P ? P : 'style' extends keyof P ? WithCSSVars<P> : P : never;A template-literal key does not satisfy string extends keyof P, so such a prop bag falls through to WithCSSVars. If that guard is the intended escape hatch for index-signature-bearing targets, it may need to recognise template-literal keys too.
Impact and workaround
skipLibCheck: true clears it in a small project, but that is a broad concession for a library-side type error, and it does not fully solve the problem in a real codebase: with skipLibCheck on, the same TS2590 reappears in user code wherever a styled component is picked conditionally, e.g.
// TS2590 at the JSX call site
const Wrapper = props.tiny ? StyledTinyWrapper : StyledWrapper;
return <Wrapper />;Annotating those away (const Wrapper: typeof StyledWrapper = …) works, but has to be repeated per site.
Happy to test a patch against our codebase (a ~3,300-test component library) if that would help.
Source: styled-components/styled-components