Вывести интерфейс Prop из styled-component

Sep 25 2020

Я стараюсь по возможности вывести Propsинтерфейс своих компонентов, а не экспортировать их. Это не проблема с классами и функциональными компонентами, но если я попытаюсь вывести Propsинтерфейс a styled-component, то опора будет типизирована, anyчто не идеально.

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

Поскольку я Boxявляюсь a styled-component, я не могу правильно вывести его интерфейс Props. Когда я создаю другой компонент, который пытается использовать предполагаемый тип Props, он проявляется как любой:

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

Ответы

HassanNaqvi Sep 25 2020 at 05:27

Вы можете определить такой тип:

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

Тип вашего компонента в стиле Box отображается как StyledComponent<"div", any, Props, never>. Таким образом, вы можете использовать inferключевое слово машинописного текста для извлечения Props из определения этого типа.

import {StyledComponent} from "styled-components";

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

type BoxInferredProps = StyledComponentProps<typeof Box>;

Теперь, как и ожидалось, вы получаете ошибку машинописного backgroundColorтекста при попытке назначить на OtherBox.

1 RamonBalthazar Nov 17 2020 at 19:02

styled-components экспортирует для этого вспомогательную функцию Typescript.

Это называется 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" />;
}

Этот код работает ☝️