I'm currently building a pattern library in which I've built a Button
component using React
and styled-components
.
Based on the Button
component, I want all my Link
s component to look exactly the same and receive exactly the same props.
For that purpose, I'm using the as
prop from styled-components
, which allows me to use that already built element as another tag or component.
Button Component
import * as React from 'react'
import { ButtonBorderAnimation } from './ButtonAnimation'
import { ButtonProps, ButtonVariant } from './Button.types'
import { ButtonBase, LeftIcon, RightIcon } from './Button.styled'
function Button({
variant = ButtonVariant.Filled,
children,
leftIcon = null,
rightIcon = null,
...props
}: ButtonProps): JSX.Element {
return (
<ButtonBase variant={variant} {...props}>
{variant !== ButtonVariant.Ghost ? (
<ButtonBorderAnimation {...props} />
) : null}
{leftIcon ? <LeftIcon>{leftIcon}</LeftIcon> : null}
{children}
{rightIcon ? <RightIcon>{rightIcon}</RightIcon> : null}
</ButtonBase>
)
}
export default Button
Button Types
export interface ButtonProps {
children: React.ReactNode
variant?: 'filled' | 'outlined' | 'ghost'
size?: 'small' | 'regular'
underlinedOnHover?: boolean
leftIcon?: React.ReactNode
rightIcon?: React.ReactNode
inverse?: boolean
}
export enum ButtonVariant {
Filled = 'filled',
Outlined = 'outlined',
Ghost = 'ghost',
}
export enum ButtonSize {
Small = 'small',
Regular = 'regular',
}
Link Component
import * as React from 'react'
import Button from '../Button/Button'
import { Link as LinkRouter } from 'react-router-dom'
import { LinkProps } from './Link.types'
function Link({ to, ...props }: LinkProps): JSX.Element {
return <Button to={to} as={LinkRouter} {...props} />
}
export default Link
Link Types
import { ButtonProps } from '../Button/Button.types'
import { LinkProps } from 'react-router-dom'
type RouterLinkWithButtonProps = ButtonProps & LinkProps
export interface LinkProps extends RouterLinkWithButtonProps {}
When I do the above, this problem comes us...
Property 'to' does not exist on type 'IntrinsicAttributes & ButtonProps'.
...which makes sense because button doesn't have the prop to
which is required for the Link
component from react-router-dom
.
How will you approach something like this? Where when using the Button
the to
prop shouldn't even be in the type and when using the Link
the to
should be required.
Uses
<Button>Hello</Button>
<Link to="/somewhere">Hello</Link>
This installs the styled-components types for TypeScript as a dev dependency. There's a styled-components extension for VS Code that makes it look like we're typing actual CSS even though it's a TypeScript file. It's called vs-code-styled-components and I suggest you install it before proceeding if you haven't already.
Props are used for components in react, but the syntax will be vary depending on the type of the component. For the following examples, we will use them in functional components. At the end of the blog we will do the same examples but with class components.
This should work.
function Link(_props: LinkProps): JSX.Element {
const props = { as: LinkRouter, ..._props };
return <Button {...props} />
}
Note that TypeScript applies strict object literal assignment checks which can be loosened by assigning an object literal to a variable beforehand instead of directly passing it as, for example, an function argument, which is consistent with the React component props assignment behavior.
declare function foo(arg: { a: number }): void;
foo({ to: '', a: 1 }); // error
const arg = { to: '', a: 1 };
foo(arg); // no error
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With