通用打字稿组件继承组件道具

Posted

技术标签:

【中文标题】通用打字稿组件继承组件道具【英文标题】: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 ),可以解决一些问题,但它没有。

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

【问题讨论】:

【参考方案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) 仍然推断不正确。跨度>

以上是关于通用打字稿组件继承组件道具的主要内容,如果未能解决你的问题,请参考以下文章

通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?

需要至少一个道具传递给组件[打字稿] [重复]

打字稿如何在父组件中传递道具

svg 组件的打字稿正确类型作为道具传递

打字稿中的样式组件“as”道具和自定义道具

打字稿反应组件中的反应/道具类型eslint错误