Skip to content

fix(react): resolve plain props before the polymorphic as overload - #1529

Open
leiit wants to merge 1 commit into
callstack:masterfrom
leiit:fix/react-polymorphic-as-generic-spread
Open

leiit wants to merge 1 commit into
callstack:masterfrom
leiit:fix/react-polymorphic-as-generic-spread

Conversation

@leiit

@leiit leiit commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Since @linaria/react 8.1.0 (#1479, for #1414), spreading the result of a generic function call into a styled component fails to type-check:

import { styled } from '@linaria/react';
import { useDropzone } from 'react-dropzone';

const Input = styled.input``;

const { getInputProps } = useDropzone();
<Input {...getInputProps()} />;
//     ~~~~~~~~~~~~~~~~~~~ TS2698: Spread types may only be created from object types.

react-dropzone types it as:

getInputProps: <T extends DropzoneInputProps>(props?: T) => T

The same code compiles with @linaria/react v7 and up to v8.0.0, and with a plain <input>. Hoisting the call into a const before the JSX also works, but it is easy to hit and the error doesn't point at the cause.

Reproduced it with react-dropzone v14 and v20. The getInputProps signature is the same in both.

Summary

#1479 changed StyledComponent<T> from a plain FunctionComponent into an overloaded call signature whose first overload is generic. TypeScript cannot infer a spread of a generic call against that overload. A standalone file with only the two call-signature shapes (no Linaria) shows the same split.

This PR adds a non-generic overload without as in front of the existing ones:

(props: TProps & { as?: undefined }, context?: any): ReturnType<React.FunctionComponent<TProps>>;

The as overloads are unchanged, so #1414's typing is preserved.

How styled-components types the same thing

  • @types/styled-components v5 has two call signatures. The first is non-generic and takes as?: never | undefined, and the second is the generic <AsC …> one. That is the same structure as here: index.d.ts#L137-L151.
  • styled-components v6.1.11 has a single signature, <AsTarget … = void, ForwardedAsTarget … = void>(props: PolymorphicComponentProps<…>). types.ts#L213-L221.
  • styled-components 6.4 later split that into overloads, citing JSX spread resolution in 4c0b72a.

Test plan

  • Added a type test for the described case. It fails on the unpatched declarations and passes with the change.
  • Existing tests pass.
  • Compared the diagnostics of about 25 additional cases between the current and patched types, and they were identical: ComponentProps, Parameters, assignability to ComponentType, ElementType and FC, as pointing at a custom component, styled(StyledThing) and styled(CustomComponent), refs and event handlers, components with required own props, and spreads of plain objects.
  • Typechecked five large projects (about 1,800 to 29,000 files each) against the published 8.2.0 types and against a copy with only this overload. The diagnostics were identical in all of them, apart from the one TS2698 this PR fixes.

Spreading the result of a generic function call into a styled component
fails with TS2698 since callstack#1479 added the generic `as` overload. TypeScript
does not infer such a spread against that overload, so add a non-generic
overload without `as` first. The `as` overloads are unchanged.
@changeset-bot

changeset-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d6708b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@linaria/react Patch
@linaria/atomic Patch
@linaria/core Patch
@linaria/babel-plugin-interop Patch
linaria Patch
@linaria/postcss-linaria Patch
@linaria/server Patch
@linaria/stylelint-config-standard-linaria Patch
@linaria/stylelint Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant