【问题标题】:Infer type of props based on component passed also as prop根据传递的组件推断道具的类型,也作为道具传递
【发布时间】:2020-03-22 01:28:47
【问题描述】:

是否可以从也作为道具传递的未知组件推断道具的正确类型?

如果是已知组件(存在于当前文件中),我可以获得道具:

type ButtonProps = React.ComponentProps<typeof Button>;

但是,如果我想创建一个通用组件Box,它接受as 属性中的组件和props 属性中的组件属性。组件可以添加一些默认的props,有一些行为,没关系。基本上它类似于高阶组件,但它是动态的。

import React from "react";

export interface BoxProps<TComponent> {
  as?: TComponent;
  props?: SomehowInfer<TComponent>; // is it possible?
}

export function Box({ as: Component, props }: BoxProps) {
  // Note: it doesn't have to be typed within the Box (I can pass anything, I can control it)
  return <Component className="box" title="This is Box!" {...props} />;
}

function MyButton(props: {onClick: () => void}) {
  return <button className="my-button" {...props} />;
}

// usage:

function Example() {
  // I want here the props to be typed based on what I pass to as. Without using typeof or explicitly passing the generic type. 
  return (
    <div>
      <Box
        as={MyButton}
        props={{
          onClick: () => {
            console.log("clicked");
          }
        }}
      >
        Click me.
      </Box>
    </div>
  );
}

要求:

  • 必须在不传递泛型类型的情况下工作(可能吗?),因为它几乎可以在任何地方使用
  • 必须使用用户定义的组件 (React.ComponentType&lt;Props&gt;)
  • 如果它也适用于 react html 元素(a、按钮、链接......它们有不同的道具)会很棒,但不是必需的

【问题讨论】:

  • 我正在努力用 forwardRef 做类似的事情

标签: reactjs typescript


【解决方案1】:

您可以使用预定义的反应类型ComponentProps 从组件类型中提取道具类型。

import React from "react";

export type BoxProps<TComponent extends React.ComponentType<any>> = {
  as: TComponent;
  props: React.ComponentProps<TComponent>;
}

export function Box<TComponent extends React.ComponentType<any>>({ as: Component, props }: BoxProps<TComponent>) {  
  return <div className="box" title="This is Box!">
    <Component  {...props} />;
  </div>
}

function MyButton(props: {onClick: () => void}) {
  return <button className="my-button" {...props} />;
}

// usage:

function Example() {
  // I want here the props to be typed based on what I pass to as. Without using typeof or explicitly passing the generic type. 
  return (
    <div>
      <Box
        as={MyButton}
        props={{ onClick: () => { } }}
      ></Box>
    </div>
  );
}

Playground Link

根据您的具体用例,解决方案可能会有所不同,但基本思想是相似的。例如,您可以稍微改变一下类型,并将道具作为BoxProps 的类型参数。这样你就可以限制组件的 props 有一些你可以在 Box 组件中提供的特定属性:

export type BoxProps<TProps extends {title: string}> = {
  as: React.ComponentType<TProps>;
} & {
  props: Omit<TProps, 'title'>;
}

export function Box<TProps extends {title: string}>({ as: Component, props }: BoxProps<TProps>) {  
  return <div className="box" title="This is Box!">
    <Component title="Title from box" {...props as TProps} />;
  </div>
}

Playground Link

如果你想接受内在标签,你也可以在TComponent约束中添加keyof JSX.IntrinsicElements

export type BoxProps<TComponent extends React.ComponentType<any> | keyof JSX.IntrinsicElements> = {
  as: TComponent;
  props: React.ComponentProps<TComponent>;
}

export function Box<TComponent extends React.ComponentType<any>| keyof JSX.IntrinsicElements>({ as: Component, props }: BoxProps<TComponent>) {  
  return <div className="box" title="This is Box!">
    <Component  {...props} />;
  </div>
}

Playground Link

【讨论】:

    猜你喜欢
    • 2018-08-01
    • 2019-07-29
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多