New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[@types/styled-components] Generics in functional components (and best practise for generics in general) #39136
Comments
I am having the exact same issue. Is there any way to work around this and preserve type safety? Even being able to convert the functional component into a concrete implementation and then passing it to styled like |
same! |
I've left my current workaround at the same issue on the Maybe there's some TypeScript feature I don't know about, but I suspect that TypeScript itself isn't expressive enough to preserve the generics as is. After looking at the export type PropsOf<
Tag extends React.ComponentType<any>
> = Tag extends React.SFC<infer Props>
? Props & React.Attributes
: Tag extends React.ComponentClass<infer Props>
? (Tag extends new (...args: Array<any>) => infer Instance
? Props & React.ClassAttributes<Instance>
: never)
: never So in order to calculate the new props, it has to evaluate the original ones, and when it tries to do that I believe it just assumes the generics to be as general as possible given their constraints. If TypeScript were to allow it to remain parameterized, then for each use of an unspecified generic, TypeScript would need to include that generic in the resulting type (which there can be arbitrary amounts depending on how many generic type parameters you pass in to the parent's generics); it would probably also preclude the parent from explicitly specifying its generics, because if it did then it wouldn't be correct if any of the type parameters passed in were generic. I think this might be related to this long-unresolved issue on the TypeScript repo: microsoft/TypeScript#1213 |
I've found the next solution for me. It seems, it fulfils a need task: styled-components/styled-components#1803 (comment) Update#1: import React from 'react';
import styled, { StyledComponent } from 'styled-components';
interface Props<T> {
a: T;
}
const MyComponent: <T>(p: Props<T>) => React.ReactElement<Props<T>> = ({ a }) => <div>{a}</div>;
const FunctionalComponent: <T>(p: Props<T>) => React.ReactElement<Props<T>> = props => <MyComponent {...props} />;
function StyledFunctional<T>(): StyledComponent<React.FC<Props<T>>, {}, {}, never> {
return styled(props => <FunctionalComponent<T> {...props} />)`
color: red;
`;
}
const GenericBase = <T extends {}>({ a }: Props<T>) => {
const TypedStyledFunctional = StyledFunctional<T>();
return <TypedStyledFunctional a={a} />;
};
const Examples = () => {
return (
<>
<GenericBase<number> a={8} />
</>
);
}; |
Thanks @Naararouter ! Did some cleaning (e.g. removing import React from "react";
import styled from "styled-components";
type Props<T> = { a: T; className?: string; }
const GenericComponent: <T>(p: Props<T>) =>
React.ReactElement<Props<T>> = ({ a, ...props }) => <div {...props}>{a}</div>;
// wrapped styled-component and re-typed it works as expected
const StyledGeneric = <T extends {}>(props: Props<T>) => {
const StyledComponent = styled<React.FC<Props<T>>>(GenericComponent)`
color: red;
`;
return <StyledComponent {...props} />;
};
export const Example = () => <StyledGeneric<number> a={8} />; Warning: as @tokland found later, this solution is throwing a runtime error and the solution from @choznerol 👇 below looks much better (using |
@melounek I'm glad that my way was helpful, but...be careful, your simplification has, at least, one critical side-effect, that's why I came to my way exact. |
Thanks! -> I updated my simplified solution replacing |
@melounek Still doesn't work for my initial case with antd library :( so...I'll be glad if you will have a time to let me know where can I miss something by adapting the code below for your example. I had been trying and...Did not work out, different unpleasant type error on each steps :'( Source: import { Select } from 'antd';
import { SelectProps } from 'antd/lib/select';
import React from 'react';
import styled, { StyledComponent } from 'styled-components';
export function StyledSelect<T>(): StyledComponent<React.FC<SelectProps<T>>, {}, {}, never> {
return styled(props => <Select<T> {...props} />)`
&.ant-select {
width: 100%;
}
`;
} P.s.: to be honest, maybe, this is related with antd definition mostly. I still have not had a time to check it thoroughly, but...in any case, styled-components & typescript is here...and I think we can continue this conversation here yet :D |
@Naararouter I would do it this way: const StyledSelect = <T extends {}>(props: SelectProps<T>) => {
const StyledComponent = styled<React.FC<SelectProps<T>>>(
(Select as unknown) as React.FC<SelectProps<T>>
)`
color: red;
`;
return <StyledComponent {...props} />;
}; But I think, your solution is also OK. PS: Another option is to wrap the antd/select with styled to workaround this.
|
The generic argument for the wrapped component can be passed like Base on the example provided by OP:
P.s. I also use a similar workaround for
Environment:
|
@melounek, thanks, I tried your solution, it types fine. However, now I get the typical runtime warning that the component has been created dynamically. Am I missing something? (styled-components: 5.2.1). |
I just tested in freshly created app ((styled-components: 5.2.1 / "react-scripts": "4.0.0-next.98 / "typescript": "^4.1.0") and I didn't find any issue with starting or building the app |
You are probably right @tokland and there is something wrong with my solution. Based on what the official documentation says: But @choznerol 's solution probably solves our struggles. See my PR for your code if you see fit: https://github.com/tokland/test-styled-components/pull/1/files |
@melounek, I see, so we need to pass the generic type as |
@melounek what do you recommend in today's world where React.FC has been deprecated? |
@aaronmw You sure? I don't see any DefinitelyTyped/types/react/index.d.ts Line 545 in a837371
|
@melounek Sorry, I was thinking of its removal from create-react-app. I was reading this whole thread just yesterday after trying to figure out the right way to do things, and with so many examples of React.FC in nearly every Typescript demo or answer, I am... Confused. Here's the story of its removal from create-react-app: facebook/create-react-app#8177 |
@melounek none of the workaround seems like a good solution. Can you re-open this issue and tag the styled-component TS authors so they're aware of this issue? |
You think the the @choznerol 's solution is not good enough? - using Could you elaborate what are you missing or how exactly should be fixed the |
Perhaps i'm having a bad day and missing something obvious, but I don't see how that solution will work in this context: https://codesandbox.io/s/awesome-worker-c78m8?file=/src/App.tsx
|
@melounek guess i am having a bad day. many thanks, i appreciate it! |
The solution posted by @choznerol (-> #39136 (comment)) seems to be broken with release 5.1.10 of It's basically reverted to the former behavior as when not explicitly specifying the generic type 😒 Does anyone else experience the same problem with The sandbox @badsyntax posted earlier also has errors when upgrading to 5.1.10, see here: https://codesandbox.io/s/peaceful-stallman-seji1?file=/src/App.tsx |
What worked for me was the answer from this SO post: https://stackoverflow.com/a/67546231/9150652 interface IProduct {
id: string;
name: string;
}
const StyledFlatList = styled(FlatList as new () => FlatList<IProduct>)`
background-color: #f7f7f7;
` |
I'm trying to use style my functional components with generics, but cannot find any approach to reach that.
For class-components is working this workaround: styled-components/styled-components#1803 (comment)
but for functional one, I didn't find a way how to do that, like you can see in the example below.
@types/styled-components
package and had problems.Definitions by:
inindex.d.ts
) so they can respond.The text was updated successfully, but these errors were encountered: