Regression (6.2.0): styled.<tag>(fn) parameter (incl. theme) silently becomes `any` with @types/react ≤ 18.2.0
Summary
Since styled-components 6.2.0, the parameter of an interpolation/style function passed to a styled.<tag>(...) factory silently resolves to any when the project uses @types/react ≤ 18.2.0. This includes the theme property and any custom props declared via the generic. There is no compile error — even under strict — so typos and wrong types go completely undetected.
It worked correctly in 6.1.x and still works in 6.2.0+ if @types/react is bumped to ≥ 18.2.79, so the breakage is the interaction between styled-components' types (changed in 6.2.0) and older @types/react.
Minimal reproduction
package.json:
{
"dependencies": {
"react": "18.2.0",
"react-dom": "18.2.0",
"styled-components": "6.4.2"
},
"devDependencies": {
"@types/react": "18.0.28",
"@types/react-dom": "18.0.10",
"typescript": "5.8.3"
}
}tsconfig.json: { "compilerOptions": { "strict": true, "jsx": "react-jsx", "moduleResolution": "node", "module": "commonjs", "target": "es2019", "lib": ["dom", "esnext"], "skipLibCheck": true, "noEmit": true, "esModuleInterop": true } }
import styled from 'styled-components';
// `$x` is declared as `number`. Assigning it to a `string` MUST error.
// If it does NOT error, the destructured parameter has silently become `any`.
export const Box = styled.div<{ $x: number }>(({ $x }) => {
const leak: string = $x; // expected: TS2322. bug: no error -> `$x` is `any`
return `content: "${leak}";`;
});Run tsc --noEmit.
No DefaultTheme augmentation is required to reproduce — theme is affected identically (it resolves to any instead of DefaultTheme).
Expected
{ $x } is typed ($x: number, theme: DefaultTheme), so const leak: string = $x errors with TS2322: Type 'number' is not assignable to type 'string'.
Actual
With styled-components ≥ 6.2.0 and @types/react ≤ 18.2.0, the parameter is any, so the assignment compiles with no error. The failure is silent.
Bisect
TypeScript pinned to 5.8.3 throughout, repro file unchanged.
styled-components version (holding @types/[email protected]):
| styled-components | result |
|---|---|
| 6.1.11 | ✅ typed |
| 6.1.19 | ✅ typed |
| 6.2.0 | ❌ any |
| 6.3.x | ❌ any |
| 6.4.0 / 6.4.1 / 6.4.2 | ❌ any |
→ regression introduced in 6.2.0.
@types/react version (holding [email protected]):
| @types/react | result |
|---|---|
| 18.0.28 | ❌ any |
| 18.0.38 | ❌ any |
| 18.2.0 | ❌ any |
| 18.2.79 | ✅ typed |
| 18.3.x | ✅ typed |
→ fixed by @types/react ≥ 18.2.79.
Notes
- The standalone
csshelper (css`...${({ theme }) => ...}`) types the parameter correctly in all combinations; only thestyled.<tag>(...)factory path regresses. - The most concerning aspect is that it's silent — projects on older
@types/reactlose all prop/theme type-safety inside styled interpolations with no error or warning after upgrading past 6.1.x. If 6.2.0 began relying on a@types/react≥ 18.2.x type feature, it would be worth either documenting a minimum@types/reactor degrading more loudly thanany.
Environment
- styled-components: 6.2.0 – 6.4.2 (regression starts at 6.2.0)
- @types/react: ≤ 18.2.0 (triggers); ≥ 18.2.79 (fixed)
- typescript: 5.8.3
- react: 18.2.0
Source: styled-components/styled-components