【问题标题】:Generic typescript component inherit component props通用打字稿组件继承组件道具
【发布时间】:2019-05-22 16:05:07
【问题描述】:

我正在使用 Typescript,我想对通用组件进行 prop 验证。我想创建一个 Field 组件,它呈现标签、错误消息和作为道具传递的组件。我进行了多次尝试以使其工作,但其中一部分或另一部分都失败了。我也阅读了数十篇文章,但找不到任何人面临完全相同的问题。

这是我目前得到的:

interface IFieldProps<P> {
    is: React.ComponentType<P>;
}

class Field<P> extends React.PureComponent<IFieldProps<P>> {
    render() {
        const { is, ...rest } = this.props;

        const Component = is;

        return (
            <div>
                <Component {...rest} />
            </div>
        );
    }
}

interface ITextInputProps {
    value: string
}

class TextInput extends React.PureComponent<ITextInputProps> {
    render() {
        return (
            <input {...this.props} />
        );
    }
}

const render = () => <Field is={TextInput} />;

打字稿编译器并没有大喊 value 方法中缺少 value 属性,所以我将使 IFieldPropsP 扩展,这应该可以工作:

interface IFieldProps<P> extends P {
    is: React.ComponentType<P>;
}

编译器现在说“一个接口只能扩展一个类或另一个接口”。 好的,那我就把它变成一个类型,不用担心:

type IFieldProps<P> = P & {
    is: React.ComponentType<P>;
}

现在一切都搞砸了。它在...rest 上大喊“Rest 类型只能从对象类型创建”。它在&lt;Component /&gt; 上大喊“JSX 元素类型'组件'没有任何构造或调用签名”。最后它在&lt;Field /&gt; 上大喊value 道具丢失了——与此同时,道具上的自动完成功能在 vscode 中停止工作。 我想如果我对 P 有一些限制(例如P extends {}),可以解决一些问题,但它没有。

如何制作一个通用组件来继承作为道具传递的组件的道具?还是有些过于复杂?其他人如何解决这个问题?

【问题讨论】:

    标签: javascript reactjs typescript jsx tsx


    【解决方案1】:

    当包装的组件道具保存在另一个道具中时它可以工作(而不是与is混合):

    interface IFieldProps<P> {
      is: React.ComponentType<P>;
      props: P;
    };
    
    class Field<P> extends React.PureComponent<IFieldProps<P>> {
      render() {
          const { is, props } = this.props;
    
          const Component = is;
    
          return (
              <div>
                  <Component {...props} />
              </div>
          );
      }
    }
    
    interface ITextInputProps {
      readonly value: string;
    }
    
    class TextInput extends React.PureComponent<ITextInputProps> {
      render() {
          return (
              <input {...this.props} />
          );
      }
    }
    
    const render = () => <Field is={TextInput} props={{ value: ''} } />;
    

    我无法根据消息找到错误的来源:

    键入“选择 & 只读>,“儿童” |排除>' 不是 可分配给类型 'IntrinsicAttributes & LibraryManagedAttributes["is"], P & { children?: 反应节点; }>'。输入'选择& 只读>,“儿童” |排除>' 不是 可分配给类型 'LibraryManagedAttributes["is"], P & {孩子?:反应节点; }>'。

    【讨论】:

    • 我也想过这个解决方案,但是我觉得我必须修改组件的 API 只是因为我使用的是 Typescript。我还担心这会迫使 PureComponents 重新渲染,因为 props 会在每次渲染中发生变化。我想在不修改 API 的情况下将这种模式用于我的按钮/链接以及 &lt;Button onClick={this.doThat}&gt;Submit&lt;/Button&gt;&lt;Button is={Link} to="/somewhere"}&gt;Redirect me&lt;/Button&gt;
    • 我同意。但是,我感觉这更多地与 React 类型定义有关,而不是 TypeScript 本身。当我试图在不更改所需 API 的情况下使其工作时,帮助的是在调用端使用类型参数 (const render = () =&gt; &lt;Field&lt;ITextInputProps&gt; is={TextInput} value="foo" /&gt;) — 但包装组件的类型 (is) 仍然推断不正确。跨度>
    猜你喜欢
    • 2020-03-04
    • 2021-07-26
    • 2021-04-26
    • 2020-03-28
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    相关资源
    最近更新 更多