styled-components: [TypeScript] styled wrapper doesn't preserve generic props
TypeScript 2.9.1
Using the styled(Component) method while wrapping a component with generic props doesn’t preserve the generic argument, it simply defaults it to {}.
Reproduction
import React, { Fragment, ReactNode } from 'react';
import styled from 'styled-components';
interface Props<T> {
renderChild: (props: T) => ReactNode;
values: T[];
}
function GenericComponent<T>(props: Props<T>) {
return (
<Fragment>
{props.values.map(props.renderChild)}
</Fragment>
);
}
const ThisWorks = (
<GenericComponent
values={[{ id: 1, value: 'foo' }]}
renderChild={props => <p key={`I_${props.id}`}>{props.value}</p>}
/>
);
const StyledGenericComponent = styled(GenericComponent)`
position: relative;
`;
const ThisDoesntWork = (
<StyledGenericComponent
values={[{ id: 1, value: 'foo' }]}
renderChild={props => <p key={`I_${props.id}`}>{props.value}</p>} // Errors: Property 'id' does not exist on '{}', Property 'value' does not exist on '{}'
/>
);
Expected Behavior
TypeScript should be able to evaluate the props object passed to renderChild, in this case, it should recognize it as id: number; value: string.
Actual Behavior
When wrapped in styled, props resolves to {}. renderChild in the wrapped example throws:
Error: Property 'id' does not exist on '{}'
Error: Property 'value' does not exist on '{}'
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 29
- Comments: 35 (4 by maintainers)
For anyone having this issue, this is my workaround:
This works perfectly for me:
So far I’ve been using the following as a workaround:
In case that the generic type is known upfront we can also use:
which seems to be the casting equivalent of creating a concrete class for a given generic param. See: https://github.com/Microsoft/TypeScript/issues/3960
I wonder why the
any.Anyway, for those with scoped generics, ya’ll could use this.
ReactSlideris the name of your component.Given
StyledFoo = styled(Foo), another workaround is to write<StyledFoo<FC<FooProps<Bar>>> ... />(instead of<StyledFoo<Bar>) to achieve same type safety as<Foo<Bar>.See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/39136#issuecomment-719950054
For a solution to support extra props passed for styled components CSS, I used
Does anyone see an issue with this approach?
I have found that I can’t pass transient props when using the above workaround. Any ideas?
@waynevanson @jgdev @acro5piano I’m confused how you guys are making that work.
Say I have a generic functional component like this:
If I do the following, I get an error:
This lines up with what @melounek experienced in https://github.com/DefinitelyTyped/DefinitelyTyped/issues/39136 . Not sure how you guys are able to instantiate things this way - am I missing something?
In the case of ReactNative FlatList, this is the solution I’m using, which seems like the simplest workaround:
Although this does not add any of the of the styled-component props eg
theme,cssetc so far from ideal. Why is this ticket closed? Is there an associated ticket in the DefinatelyTyped repo?As a workaround, I’m using the
cssproperty instead of a styled component, maybe that’s an option for someone else as well:For those of guys looking for a way to generic type of Ant Design component (my case is Ant Design Table), here is my solution:
Hello! I’ve found the next solution (without
anyand another hacks), maybe, it will help someone else. I’ve came to this view when I was trying to type my components from antd library, particularly itsSelectcomponent. It can be generic. But below, I’ll put a common view of solution.Definition:
Use-case
Update#1 CAUTION!!! The implementation above has a some performance issue (thanks to @dfernandez-asapp) with creating of new styled for each render, you should just define
const TypedSelect = StyledGeneric<T>();outside of render method and where you already know an exact type (you must have this place in any case somewhere in app).Update#2 Updated version here: https://github.com/styled-components/styled-components/issues/1803#issuecomment-665752971
Hey folks, TS typings for styled-components have been moved to DefinitelyTyped, so please feel free to move your issue there where you’ll receive better community support.
any news on this? it is a blocker for us
Hi all, I have a solution that worked for me (and doesn’t involve type suggestions to
any). See the full code below.It would be nice if this could be done a) without a type suggestion, and b) retaining the generic after the
styledinvocation, though I don’t think generics can survive through TypeScript’s inference passing through a function call like that, so these might be infeasible at the moment. so I think a type suggestion will be as good as it gets for now.As an alternative, you can also use the Factory pattern to achieve the same thing without a type suggestion:
Hope this helps someone!
Yes. It will miss out on the execution props like “as”, “forwardedAs” and “theme”.
Like some of you, I’m using antd and this is the solution I came up with:
It seems that if you don’t need to use the props in the styled template you’re ok with some of the above methods.
However, if you want to use the props in the css you have to make an intermediary React Component that forwards the generic type and then wrap that in
styled()as so@badsyntax nope, emotion doesn’t work either:
then using it like this:
it compiles perfectly (should fail) and shows the following types (notice the unknown as the prop type instead of it being a generic component):
in reality it should fail since both props should be of the same type T
This works for most use-cases, but
StyledSelectnow cannot be referred to in another styled component definition:Results in this error:
TS2769: No overload matches this call. Overload 1 of 3, '(first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; }, any>>, ...rest: Interpolation<...>[]): StyledComponent<...>', gave the following error. Argument of type 'FC<void>' is not assignable to parameter of type 'Interpolation<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, any>>'. Type 'FunctionComponent<void>' is not assignable to type 'InterpolationFunction<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, any>>'. Types of parameters 'props' and 'props' are incompatible. Type 'ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, any>' is not assignable to type 'PropsWithChildren<void>'. Type 'ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; }, any>' is not assignable to type 'void'. Overload 2 of 3, '(first: TemplateStringsArray | CSSObject | InterpolationFunction<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & void & { ...; }, any>>, ...rest: Interpolation<...>[]): StyledComponent<...>', gave the following error. Argument of type 'FC<void>' is not assignable to parameter of type 'Interpolation<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; } & void & { ...; }, any>>'. Type 'FunctionComponent<void>' is not assignable to type 'InterpolationFunction<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; } & void & { ...; }, any>>'. Type 'ReactElement<any, any> | null' is not assignable to type 'Interpolation<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; } & void & { ...; }, any>>'. Type 'ReactElement<any, any>' is not assignable to type 'Interpolation<ThemedStyledProps<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> & { ...; } & void & { ...; }, any>>'. Type 'ReactElement<any, any>' is not assignable to type 'CSSObject'. Index signature for type 'string' is missing in type 'ReactElement<any, any>'.
I don’t think these PRs are gonna fix this issue. The problem here is here:
const StyledGenericComponent = styled(GenericComponent). With this,styleddoes not expect a generic component, it expects a concrete component (with concrete prop type). In general, this is almost impossible to distinguish a generic component from a regular one when it comes to higher-order functions. I can think of how it can be worked around, but you can read this TS issue where such problems are discussed: https://github.com/Microsoft/TypeScript/issues/9366