Interface Infer Prop à partir du composant stylé

Sep 25 2020

J'essaie de déduire l' Propsinterface de mes composants en les exportant chaque fois que possible. Ce n'est pas un problème avec la classe et les composants fonctionnels, mais si j'essaie de déduire l' Propsinterface de a styled-component, l'accessoire est typé anyce qui n'est pas idéal.

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>
    </>
  );
}

En Boxétant a styled-component, je ne peux pas en déduire correctement son interface Props. Lorsque je crée un autre composant qui utilise des tentatives d'utilisation du type Props inféré, il apparaît comme n'importe quel:

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

Réponses

HassanNaqvi Sep 25 2020 at 05:27

Vous pouvez définir un type comme celui-ci:

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>

3 LindaPaiste Sep 25 2020 at 05:28

Le type de votre composant de style Box s'affiche sous la forme StyledComponent<"div", any, Props, never>. Vous pouvez donc utiliser le infermot - clé dactylographié pour extraire les Props de cette définition de type.

import {StyledComponent} from "styled-components";

type StyledComponentProps<T> = T extends StyledComponent<any, any, infer P, any> ? P : never

type BoxInferredProps = StyledComponentProps<typeof Box>;

Vous obtenez maintenant une erreur de typographie, comme prévu, lorsque vous essayez d'attribuer backgroundColorsur OtherBox.

1 RamonBalthazar Nov 17 2020 at 19:02

styled-components exporte un assistant Typescript pour cela.

Ça s'appelle 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" />;
}

Ce code fonctionne ☝️