Search code examples
reactjstypescriptstyled-components

Infer Prop interface from styled-component


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


Solution

  • 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>