【问题标题】:Infer Prop interface from styled-component从 styled-component 推断 Prop 接口
【发布时间】:2021-01-11 05:16:30
【问题描述】:

我尝试推断我的组件的Props 接口而不是尽可能导出它们。这不是类和函数组件的问题,但如果我尝试推断styled-componentProps 接口,则prop 类型为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>
    </>
  );
}

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

【问题讨论】:

    标签: reactjs typescript styled-components


    【解决方案1】:

    您的 Box 样式组件的类型显示为 StyledComponent&lt;"div", any, Props, never&gt;。因此,您可以利用 typescript 的 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>;
    

    现在,当您尝试在 OtherBox 上分配 backgroundColor 时,正如预期的那样,您会遇到打字稿错误。

    【讨论】:

    • 这是一次绝妙的尝试!不幸的是,它不会返回样式化的 react 组件的实际 props 的类型。此外,styled-components 将其中的大部分导出为StyledComponentInnerOtherProps。不过很酷!
    • @HassanNaqvi 你能描述一下这个问题吗? BoxInferredProps 正在解析为 Props,并且智能感知和编译器错误按预期工作。为什么这个答案还不够?
    • 更新 - 啊哈!当然,还有 HTML 元素固有的道具 - aria 属性、事件处理程序等......这个答案确实很好地满足了我最初的问题,但@HassanNaqvi 的答案更强大。
    【解决方案2】:

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

    此代码有效☝️

    【讨论】:

    • 这似乎不包括“主题”道具
    • 这不包括内联定义的自定义道具,例如:styled.input&lt;{error?: boolean}&gt;""
    【解决方案3】:

    你可以这样定义一个类型:

    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>

    【讨论】:

      【解决方案4】:

      我在这个线程中尝试了各种示例,发现没有一个建议的解决方案适用于我想到的所有用例。

      我希望能够推断出这些组件的 props:

      1. 常规组件
      2. 样式化组件
      3. 带有自定义道具的样式化组件
      4. 原版组件,例如 div、输入等。

      我能够使用这种类型帮助器涵盖所有用例:

      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
      

      现在您可以为所有这些用例获得正确的道具类型:

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

      【讨论】:

      • 当我使用 .attrs 时,这对我不利,因为我在那里插入的属性是必需的。我不得不重写:``` typescript export type InferComponentProps = T extends AnyStyledComponent ? ( ComponentPropsWithoutRef> & Omit, StyledComponentInnerAttrs> ) : T extends keyof JSX.IntrinsicElements | JSXElementConstructor ? ComponentPropsWithoutRef :从不; ```
      猜你喜欢
      • 1970-01-01
      • 2021-07-13
      • 2019-07-28
      • 1970-01-01
      • 2017-05-17
      • 2021-02-19
      • 1970-01-01
      • 2020-04-23
      • 2021-07-03
      相关资源
      最近更新 更多