There are several questions about this error on SO but none from anybody in a similar situation and none of the solutions I have found work so I'm going to post a specific question.
When using a Styled component inside another component and passing props to it, do I have to create a new type to pass the props through to the styled component or is there some way of using the existing styled component types?
Here is my code. It seems to be the as="" attribute which is giving the error above. I assume this is something to do with the fact that my component only takes the prop so it can pass it to the Styled Component as it is a feator of Styled Components.
import React from 'react'
import styled from 'styled-components'
type Props = {
additionalClass?: string,
as?: string
}
const Component: React.FC<Props> = (props) => {
return (
<StyledComponent as={props.as} className={props.additionalClass}>
{props.children}
</StyledComponent>
)
}
export default Component
const StyledComponent = styled.div`
...styles go here
`
You just need to validate the prop when you are creating the styled.div
Example
const Menu= styled.div<{ menuOpen: boolean }>`
${({ menuOpen }) => {
if (menuOpen) {
return 'background-color: white';
}
return 'background-color: black';
}}
`;
export const Component: React.FC = () => {
const [menuOpen, setMenuOpen] = useState(false);
return(
<Menu menuOpen={menuOpen}>
whatever content
</Menu>
)
}
The problem was that I was trying to pass the "as" prop to styled components and I thought it needed to be a string so I did this...
type Props = {
className?: string,
as?: string
}
The problem is that once you pass it to styled-components it expects it to be the name of an element. I found the answer on Github and it can be one of two different things. This works...
type Props = {
className?: string,
as?: React.ElementType | keyof JSX.IntrinsicElements
}
type Props = {
additionalClass?: string,
[key: string]: any
}
Related
I'm trying to build a Link component that defaults as an a tag but will allow a user to pass React Router DOM or Next.JS Link components and remain strongly typed.
I've been stuck on this for a while. I'm shocked that I haven't been able to find others with the same issue but maybe I'm just not searching for the right things.
Here is my code
export const NewLinkStyles = styled.a`
align-items: center;
color: ${({ theme }: { theme: Theme }) => theme.primary.step10};
`
export type CustomLinkProps<C extends React.ElementType = 'a'> = {
as?: C;
Icon?: IconType;
selected?: boolean;
disabled?: boolean;
} & React.ComponentPropsWithoutRef<C>;
export type LinkProps<C extends React.ElementType> = React.PropsWithChildren<
CustomLinkProps<C>
> &
React.ComponentPropsWithoutRef<C> &
Omit<React.ComponentPropsWithoutRef<C>, keyof CustomLinkProps<C>>;
export const NewLink = <C extends React.ElementType>({
as,
children,
...restProps
}: LinkProps<C>) => {
return <StyledNewLink {...restProps}>{children}</StyledNewLink>;
};
And my expected results.
import { Link } from "react-router-dom";
export const TestComp = () => {
return (
<div>
<NewLink as={'a'} href="https://google.com"> // should alert if try to use to='/home'
Hi
</NewLink>
<NewLink as={Link} to="https://google.com"> // should allert if you try to use href
Hi
</NewLink>
</div>
);
};
I feel like I should use one of the interfaces that come with styled components but wasn't having any luck. Thank you so much for your help! This has been driving me bonkers!
I have assigned type React.FC to my component but I still dont't have access to the children props. I get this error "Property 'children' does not exist on type 'ButtonProps'". Is there another reason why I can't access the children props?
import React from 'react'
export interface ButtonProps{
label:string;
}
const Button:React.FC<ButtonProps> = (props) => {
const {children} = props
return <div>{children}</div>
}
export default Button
Using React.FC is discouraged, so for now you should define the type of your component and be explicit about its children prop like this:
export interface ButtonProps{
label:string;
children: React.ReactNode
}
const Button = (props: ButtonProps) => {
const {children} = props
return <div>{children}</div>
}
Off topic: Do not pass to your component something your component will not use, try to destructure props in the head of your component. Thus, the component will get only what it needs, and you will know exactly what you are passing to the component, what are their props and default values if needed.
In typescript interface, we can define, like :
export interface ViewContainerProps {
style?: ViewStyle | ViewStyle[];
children?: React.ReactNode
aString:string
aNumber:number
}
I want to write a custom top-level <View>, to cover all screen, it look like this:
const VIEW_CONTAINER: ViewStyle = {
width: windowWidth,
height: windowHeight,
};
export interface ViewContainerProps {
style?: ViewStyle | ViewStyle[];
children?: React.ReactNode ///no , something wrong
}
export const ViewContainer = (props: ViewContainerProps) => {
const {style, children} = props;
return <View style={[style, VIEW_CONTAINER]}>{children}</View>;
};
And i want to children return React component to interact and do anything that a react component do, what type of React can i put in typescript interface? I read some docs , say that React.reactNode a light, stateless, immutable, virtual representation of a DOM node, feel like it not what i want when create top-level components that wrap all child components
Please help, thank you a lots
React.ReactNode is the correct type. One way to be sure of that is by looking at the build in type for functional components React.FC, you can see that it types the children prop as React.ReactNode.
Rather than adding the children prop yourself, you should use React.FC for your component:
import React from "react"
export interface ViewContainerProps {
style?: ViewStyle | ViewStyle[];
}
export const ViewContainer: React.FC<ViewContainerProps> = (props) => {
const { style, children } = props;
return <View style={[style, VIEW_CONTAINER]}>{children}</View>;
};
You can use React.FunctionComponent or React.FC
import React, { FC } from 'react';
export const ViewContainer: FC = (props: ViewContainerProps) => {
const {style, children} = props;
return <View style={[style, VIEW_CONTAINER]}>{children}</View>;
};
I'm trying to create a wrapper for my styled component but I'm having trouble getting the types right.
Let's say I have a styled component like this:
const Button = styled.button<ButtonProps>`
background-color: ${(props) => props.color};
`;
Now I want to create a wrapper component that contains this styled button, eg. like this:
const WrappedButton: React.FunctionComponent<ButtonProps> = ({ children, ...rest }) => (
<div>
<Button {...rest}>{children}</Button>
</div>
);
Now this all works fine, but what I actually want is the WrappedButton component to accept all props that the Button component would accept and pass them along to the wrapped Button component.
So for example I want this to compile, as type is a valid prop of a HTML button element (and therefore also a valid prop of the Button component, but not when the Button component is wrapped):
// TypeScript will throw an error because "type" is not a valid prop of WrappedButton.
const MyComponent = () => <WrappedButton type="submit">Test</WrappedButton>
I know I can make "type" a prop of the WrappedComponent, but that's not the point, I want the WrappedComponent to accept all props that a normal HTML button would accept.
EDIT: Also I need all styled-component specific props on the wrapped component, such as the as prop of styled-components. Here's an updated version of the code sandbox: https://codesandbox.io/s/react-typescript-styled-components-forked-3o20j?file=/src/index.tsx
I have tried so many things but TypeScript is always complaining. I have also searched the docs and the internet but have not found anything.
I believe You are asking about React.ButtonHTMLAttributes<HTMLButtonElement>.
import React from 'react'
import styled from 'styled-components'
const Button = styled.button<ButtonProps>`
background-color: ${(props) => props.color};
`;
type ButtonProps = {
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
const WrappedButton: React.FunctionComponent<ButtonProps> = ({ children, ...rest }) => (
<div>
<Button {...rest}>{children}</Button>
</div>
);
If you want to use raw html <button>, you mignt be interested in: JSX.IntrinsicElements["button"];
Check this answer
UPDATE
The quick and dirty solution would be :
type ButtonProps = {
} & React.ButtonHTMLAttributes<HTMLButtonElement> & { as?: string | React.ComponentType<any> };
But it is not generic.
There is StyledComponentPropsWithAs type in SC typings, but it is not exported (
If you're trying to suppress native attributes you could do something like this:
interface Props {
type?: 'error' | 'success'
}
type OmitNativeAttrs = Omit<React.HTMLAttributes<HTMLButtonElement>, keyof Props>
export type ButtonProps = Props & OmitNativeAttrs
I'm missing something here with the validation how to add types validation? Having error "element 'children' implicitly has an 'any' type".
import * as React from 'react';
import Button from './Styles';
const Button1 = ({ children, ...props }) => (
<Button {...props}>{children}</Button>
);
Button1.propTypes = {};
export default Button1;
Edit 2022:
with react 18, FC no longer provides children, so you have to type it yourself, and you can drop FC:
import React, { ReactNode } from "react";
interface Props {
children?: ReactNode
// any props that come into the component
}
const Button1 = ({ children, ...props }: Props) => (
<Button {...props}>{children}</Button>
);
Yes you are missing a type for Props as whole, which means typescript sees it as any and your ts rules dont allow it.
You have to type your props as:
import React, { FC } from "react";
interface Props {
// any props that come into the component
}
const Button1: FC<Props> = ({ children, ...props }) => (
<Button {...props}>{children}</Button>
);
This was a major issue for me and I wasted a lot of time figuring out the correct solution.
Right now you have an error with the children prop but in the future, you might have this error for a lot of functions where you are destructuring the params.
So I would suggest, follow this GitHub issue.
const yourfunc = ({destructuredProps}: {destructuredProps: type}) => {}
You can also add the predefined type to your functional components like this:
const Button1: React.FC<{}> = ({ children }) => (
<Button>{children}</Button>
);
By this way you don't have to repeat yourself to define children props.
The fuller version could be like this:
interface Props {
// any other props that come into the component, you don't have to explicitly define children.
}
const Button: React.FC<Props> = ({ children, ...props }) => {
return (
<Button {...props}>{children}</Button>
);
};
Please note it works for React 16.8
As another approach, you can use the built-in generic type "React.PropsWithChildren" for children in props, taking those props accordingly. A very short code would look like this:
import React from "react";
import Button from "./Styles";
type MyComponentProps = React.PropsWithChildren<{}>;
export default function MyComponent({ children, ...other}: MyComponentProps) {
return <Button {...other}>{children}</Button>;
}
You can use types as well
type ButtonProps = {
children: ReactNode;
}
const Button = ({ children }: ButtonProps) => (
<button>{children}</button>
);
I find it best to have the component props interface extend from React.HTMLAttributes because it gives you the standard HTML attributes without any extra configuration:
interface Button1Props extends React.HTMLAttributes<Element> {
// add any custom props, but don't have to specify `children`
}
const Button1 = ({ children, ...props }: Button1Props) => (
<Button {...props}>{children}</Button>
)
If you want to enforce children to be provided, you can make it required by redefining it in the props interface:
interface Button1Props extends React.HTMLAttributes<Element> {
children: React.ReactNode
// add any custom props, but don't have to specify `children`
}
const Button1 = ({ children, ...props }: Button1Props) => (
<Button {...props}>{children}</Button>
)
Also do this to stop error: children?: React.ReactNode;
export interface props {
children?: React.ReactNode; }
const Screen = ({ children }: props) => {
return (
<div style={{ margin: '2%' }}>
{children}
</div>
); };
export default Screen;
This error can be fixed by explicitly defining type for the variable (children in this case) and not leaving it to be implicitly inferred
Error can be stopped altogether with TypeScript --noImplicitAny compiler option
It needs 'any' so give it !
const Button1 = ({ children, ...props } : any)
Then handle it in function if you want to preserve the type
One answer I found and wanted to share, but will probably be highly frowned upon, is that you can set skipLibCheck to true in your tsconfig.json within the compilerOptions object, and it will ignore the issue.
As a caveat, please ensure you know what you are doing when adding this rule. A good article about this is here: https://www.testim.io/blog/typescript-skiplibcheck/