Reputation: 16733
I try to infer my components' Props
interface over exporting them whenever possible. This isn't an issue with class and functional components, but if I try to infer the Props
interface of a styled-component
, the prop is typed to any
which is not ideal.
interface Props {
bgColor: string;
children: React.ReactNode;
}
const Box = styled.div<Props>`
background-color: ${(p) => p.bgColor};
`;
const Button = (props: Props) => (
<button style={{ backgroundColor: props.bgColor }}>{props.children}</button>
);
type ButtonInferredProps = React.ComponentProps<typeof Button>;
type BoxInferredProps = React.ComponentProps<typeof Box>;
const OtherBox = (props: BoxInferredProps) => (
<div style={{ backgroundColor: props.bgColor }}>{props.children}</div>
);
const OtherButton = (props: ButtonInferredProps) => (
<button style={{ backgroundColor: props.bgColor }}>{props.children}</button>
);
export default function App() {
return (
<>
<Box bgColor="red">Hi! I'm a box! </Box>
<OtherBox bgColor="purple" backgroundColor="red">
Hi! I'm another box
</OtherBox>
<Button bgColor="blue">Hi! I'm a button</Button>
<OtherButton bgColor="green">Hi! I'm another button</OtherButton>
</>
);
}
With Box
being a styled-component
, I can't properly infer its Props interface. When I create another component that uses attempts to use the inferred Props type, it comes through as any:
const OtherBox = (props: BoxInferredProps) => (
{/* TS won't complain that `props` doesn't have a `iAmNotTyped` property which is desired... */}
<div style={{ backgroundColor: props.iAmNotTyped}}>{props.children}</div>
);
https://codesandbox.io/s/styled-components-typescript-forked-7cq4q?file=/src/App.tsx
Upvotes: 9
Views: 2022
Reputation: 9416
I've tried various examples in this thread and found that none of the proposed solutions work for all the use cases I have in mind.
I want to be able to infer props of these kinds of components:
I was able to cover all the use cases with this type helper:
import { ComponentPropsWithoutRef, JSXElementConstructor } from "react"
import {
AnyStyledComponent,
StyledComponentInnerComponent,
StyledComponentInnerOtherProps,
} from "styled-components"
export type InferComponentProps<T> =
T extends AnyStyledComponent
? ComponentPropsWithoutRef<StyledComponentInnerComponent<T>> &
StyledComponentInnerOtherProps<T>
: T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>
? ComponentPropsWithoutRef<T>
: never
Now you get correct prop types for all of these use cases:
const StyledComponent = styled.input ``
const AdvancedStyledComponent = styled.input<{ customProp: boolean }> ``
const RegularComponent = (props: { customProp: boolean }) => null
InferComponentProps<typeof StyledComponent>
InferComponentProps<typeof AdvancedStyledComponent>
InferComponentProps<typeof RegularComponent>
InferComponentProps<"input">
Upvotes: 1
Reputation: 4260
styled-components
exports a Typescript helper for this.
It's called StyledComponentPropsWithRef
:
import React from 'react';
import styled, { StyledComponentPropsWithRef } from 'styled-components';
const RedLink = styled.a`
color: red;
`;
type Props = StyledComponentPropsWithRef<typeof RedLink>;
const Link = (props: Props) => <RedLink {...props} />;
export default function App() {
return <Link href="/" role="button" />;
}
This code works ☝️
Upvotes: 6
Reputation: 42218
The type for your Box styled component is showing up as StyledComponent<"div", any, Props, never>
. So you can make use of typescript's infer
keyword to extract Props from that type definition.
import {StyledComponent} from "styled-components";
type StyledComponentProps<T> = T extends StyledComponent<any, any, infer P, any> ? P : never
type BoxInferredProps = StyledComponentProps<typeof Box>;
Now you get a typescript error, as expected, when trying to assign backgroundColor
on OtherBox.
Upvotes: 3
Reputation: 1061
You can define a type like this:
import {
StyledComponentInnerComponent,
StyledComponentInnerOtherProps,
AnyStyledComponent
} from 'styled-components';
type inferStyledTypes<T extends AnyStyledComponent> =
React.ComponentProps<StyledComponentInnerComponent<T>>
& StyledComponentInnerOtherProps<T>;
// Use the above type to infer the props:
type BoxInferredProps = inferStyledTypes<typeof Box>
Upvotes: 2