styled-components / styled-components

Visual primitives for the component age. Use the best bits of ES6 and CSS to style your apps without stress 💅
https://styled-components.com
MIT License
40.11k stars 2.48k forks source link

Using a generic component results in TS error for the generic argument #4246

Open mdjzon opened 4 months ago

mdjzon commented 4 months ago

Reproduction

Steps to reproduce

https://codesandbox.io/p/sandbox/wizardly-kapitsa-nxcwtz?file=%2Fsrc%2FApp.tsx%3A22%2C39

Expected Behavior

No TS errors.

Actual Behavior

Using a styled generic component results in the following TS error:

TS2344: Type XXX does not satisfy the constraint void | WebTarget

This started to happen in v6.1.3. Still a problem in v6.1.6. Works fine in v6.1.1.

Possibly related to this PR: https://github.com/styled-components/styled-components/pull/4236

quantizor commented 4 months ago

The styled wrapper exposes a generic that intercepts passed props in order to determine a runtime target and extract types. I don't think a generic component assembled in this fashion will work. Have you considered using the as prop to accomplish your goal? That's the design intent of that API to switch what's being rendered without messing with styles.

mdjzon commented 4 months ago

Maybe my example was not comprehensive enough, or a bit misleading. The generic props in our use-cases are not related to what's being rendered by the styled component (there the as prop would be useful, as you mentioned). I changed the codesandbox example to illustrate the same problem but in a different context where the generic prop is used for specifying the interface representing the values in a form (backed by react-hook-form). Hope this is more clear.

But I just realised that it has actually has never worked - in v6.1.1 the type information was stripped "silently" and thus TS did not complain. The recent changes in v6.1.3 only made it more obvious by producing an actual TS error.

So if it is not possible for you to solve (allowing styled generic components) I'll just have to find a different solution. This issue can be closed in that case..

quantizor commented 4 months ago

Just another thought, you could provide an untyped initial component and then retype it using as. e.g.

<StyledField as={Field<FormValues>} name="value2" label="Value 2" />

It's equivalent to your sandbox but types properly since it doesn't try to override the styled() wrapper generic.

xavierstampslafont commented 3 months ago

I have this issue as well. Casting using as is an option. Another option is to declare the type explicitly, e.g. from mdjzon's Codesandbox:

const StyledField: typeof Field = styled(Field)`
  color: deeppink;
`;
BohdanPylypchuk commented 3 months ago

@mdjzon If you have a static type (FormValues) you can set it inside the styled function with the component:

const StyledField = styled(Field<FormValues>)
  color: deeppink;
;
beaucollins commented 2 months ago

We used to use satisfies and 6.1.8 breaks it for us. Same typescript error about WebTarget.

const Comp = <T,>(props: {
  className?: string,
  onOpen:(item: T) => void,
  items: T[]
}): JSX.Element | null =>  {
 return <div className={props.className}>{/* implementation  */}</div>;
}

cons StyledComp = styled(Comp)`` satisfies typeof Comp;

image

This is during upgrading 6.1.0 to 6.1.8.


This is a workaround that keeps the generic correctly but I'd love not to have to do this. It looks like this is becase styled(fn) itself gained a generic.


type StyledComponent = Styled<'web', any, any, any>;

const StyledComboboxMenu = styled(ComboboxMenu)`
  width: 340px;
` satisfies StyledComponent & typeof ComboboxMenu;

Workaround iteration 2 with no any.


type StyledComponent = Styled<
  'web',
  WebTarget,
  Record<never, never>,
  Record<never, never>
>;

const StyledComboboxMenu = styled(ComboboxMenu)`
  width: 340px;
` satisfies StyledComponent & typeof ComboboxMenu;

image

codesandbox

joshuaellis commented 2 weeks ago

Happy to make a new issue if you don't feel like this is the same, but I have a similar issue with components not having generics respected correctly so their props are not correctly defined:

TS playground link