【问题标题】:Specify specific props and accept general HTML props in Typescript React App在 Typescript React App 中指定特定的道具并接受一般的 HTML 道具
【发布时间】:2019-01-20 23:13:44
【问题描述】:

我有一个 React Wrapper 组件,它接受一些道具,但将所有其他道具转发给子组件(尤其是与 className、id 等原生道具相关)。

但是,当我传递原生道具时,Typescript 会抱怨。查看错误信息:

TS2339:类型上不存在属性“className” 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly & 只读'。

我怎样才能得到一个带有特定 props 的组件也接受原生 props( 不接受任何 props 并放弃类型检查)?

我的代码如下所示:

interface WrapperProps extends JSX.IntrinsicAttributes {
  callback?: Function
}

export class Wrapper extends React.Component<WrapperProps>{
  render() {
    const { callback, children, ...rest } = this.props;
    return <div {...rest}>
      {children}
    </div>;
  }
}

export const Test = () => {
  return <Wrapper className="test">Hi there</Wrapper>
}

仅供参考:我在这里发现了一个类似的问题,但答案基本上放弃了类型检查,我想避免这种情况:Link to SO-Question

【问题讨论】:

    标签: javascript reactjs typescript react-props


    【解决方案1】:

    我们可以看看div props 是如何定义的:

    interface IntrinsicElements {
        div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
    }
    

    如果我们使用React.DetailedHTMLProps&lt;React.HTMLAttributes&lt;HTMLDivElement&gt;, HTMLDivElement&gt; 作为基本类型,我们将拥有div 的所有属性。由于DetailedHTMLProps 只是将ref 添加到React.HTMLAttributes&lt;HTMLDivElement&gt;,我们可以使用它作为基本接口来获取所有div 属性:

    interface WrapperProps extends React.HTMLAttributes<HTMLDivElement> {
      callback?: Function
    }
    
    export class Wrapper extends React.Component<WrapperProps>{
      render() {
        const { callback, children, ...rest } = this.props;
        return <div {...rest}>
          {children}
        </div>;
      }
    }
    
    export const Test = () => {
      return <Wrapper className="test">Hi there</Wrapper> // works now
    }
    

    【讨论】:

    • 非常感谢!这似乎有点冗长。有没有其他“更多反应”或“更多打字稿”的方式来做到这一点?这似乎是一个非常常见的用例,扩展 HTMLBaseAttributes 似乎有点矫枉过正。但是答案解决了问题,所以谢谢!
    • @SergejHerbert 我打算删除 DetailedHTMLProps 部分,现在将其删除。据我所知,这是最短的打字稿方式:)
    • @SergejHerbert 另一种方法是为道具使用交集类型:class Wrapper extends React.Component&lt;WrapperProps &amp; React.HTMLAttributes&lt;HTMLDivElement&gt;&gt;
    • 如果你的组件渲染的原生元素是通过一个prop确定的:function MyComponent({ elementName: Component, children, ...props }) { return (&lt;Component {...props}&gt;{children}&lt;/Component&gt;); },因为需要一个类型参数,你将如何做到这一点?
    • @webbower 我遇到了类似的问题,对于一个通用的 HTML 元素,我使用过:interface WrapperProps extends React.HTMLAttributes&lt;HTMLElement&gt;
    【解决方案2】:

    我的一位同事想通了。在此处分享以扩大知名度:

    interface ComponentPropTypes = {
      elementName?: keyof JSX.IntrinsicElements; // list of all native DOM components
      ...
    }
    
    
    // Function component
    function Component({
      elementName: Component = 'div',
      ...rest,
      // React.HTMLAttributes<HTMLOrSVGElement>) provides all possible native DOM attributes
    }: ComponentPropTypes & React.HTMLAttributes<HTMLOrSVGElement>)): JSX.Element {
      return <Component {...rest} />;
    }
    
    // Class component
    class Component extends React.Component<ComponentPropTypes & React.HTMLAttributes<HTMLOrSVGElement>> {
      render() {
        const {
          elementName: Component,
          ...rest,
        } = this.props;
        return <Component {...rest} />
      }
    }
    

    【讨论】:

      【解决方案3】:

      JSX.IntrinsicElements 有这个信息,例如

      const FooButton: React.FC<JSX.IntrinsicElements['button']> = props => (
        <button {...props} className={`foo ${props.className}`} />
      )
      
      // alternative...
      const FooButton: React.FC<React.PropsWithoutRef<
        JSX.IntrinsicElements['button']
      >> = props => <button {...props} className={`foo ${props.className}`} />
      

      react-typescript-cheatsheet 项目中发现了这一点。

      【讨论】:

        【解决方案4】:

        查看ComponentPropsComponentPropsWithRefComponentPropsWithoutRef - 这将接受可以是"div""button" 或任何其他组件的通用输入。它将包括反应特定的道具,例如className

        import React, {
          forwardRef,
          ComponentPropsWithoutRef,
          ComponentProps,
          ComponentPropsWithRef
        } from "react";
        
        const ExampleDivComponent = forwardRef<
          HTMLDivElement,
          ComponentPropsWithoutRef<"div">
        >(({ children, ...props }, ref) => {
          return (
            <div {...props} ref={ref}>
              {children}
            </div>
          );
        });
        
        <ExampleDivComponent
          className=""
          style={{ background: "green" }}
          tabIndex={0}
          onTouchStart={() => alert("touched")}
        />;
        
        const ExampleButtonComponent: React.FC<ComponentProps<"button">> = ({
          children,
          ...props
        }) => {
          return <button {...props}>{children}</button>;
        };
        
        <ExampleButtonComponent onClick={() => alert("clicked")} />;
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-25
          • 2019-11-14
          • 1970-01-01
          • 2022-01-23
          • 2021-04-01
          • 1970-01-01
          相关资源
          最近更新 更多