首页 > 解决方案 > Typescript:联合类型组件道具的 HOC 道具类型

问题描述

我有类似道具的组件:

setSelectedValue: (value: number | string) => void;

我想做 HOC,它有 setSelectedValue 道具,只能传递数字,也可以作为道具传递给原始组件,我看到它的类型如下:

setSelectedValue: (value: number) => void;

当我尝试做到这一点时,我收到 TS 错误消息:

Type '(value: number) => void' is not assignable to type '(value: number | string) => void'.

我怎样才能得到想要的结果?

标签: reactjstypescript

解决方案


假设您的组件(comp.tsx):

import React from 'react';
import hocComp from './hocComp';

interface Props {
  setSelectedValue: (value: number | string) => void; // number | string
}

class Comp extends React.Component<Props> {
  render() {
    return <div>Comp</div>;
  }
}

export default hocComp<Props>(Comp); // LINE 1

和 HOC ( hocComp.tsx ):

import React from 'react';

interface Props {
  setSelectedValue: (value: number) => void; // number // LINE 2
}

export default function hocComp<P extends object>(
  WrappedComponent: React.ComponentType<P>
) {
  return class extends React.Component<Props> {  // LINE 3  
    render() {
      return <WrappedComponent {...this.props as P} />; // LINE 4
    }
  };
}

以上面显示的方式定义,您可以在没有任何TypeError的情况下使用它(在 App.tsx 中) :

import React from 'react';
import Comp from './comp';

export default function App() {
  function setSelectedValue(value: number) {
    console.debug(value);
  }

  return (
    <div className="App">
      <h1>Hello World</h1>
      <Comp setSelectedValue={setSelectedValue} />
    </div>
  );
}

这是CodeSandbox


这是一个解释:

  1. 在调用 HOC时,我们将组件及其Props接口传递给 HOC。

    export default withComp<Props>(Comp); // LINE 1

  2. HOC 的定义:我们将传递的接口接收到一个泛型变量P中,并且传递的组件WrappedComponent将其类型声明为React ComponentType,其中 P 作为 props 接口

    export default function withComp<P extends object>( WrappedComponent: React.ComponentType<P> ) {

  3. 最后,在 HOC 的内部渲染:我们调用传递的组件WrappedComponent并将所有具有在 HOC 本身(第 2 行)中定义的接口的道具传递给它 - (请注意,这些道具在 HOC 返回的类中使用 - 第 3 行)

    return <WrappedComponent {...(this.props as P)} />; // LINE 4

  4. 现在你可以看到P has -> setSelectedValue: (value: number | string) => voidProps has -> setSelectedValue: (value: number) => void应该抛出TypeError,我们通过类型转换避免了它作为第 4 行。你可以尝试删除这个类型转换,它会开始显示错误。

您还可以查看这篇文章以了解TypeScript 中的泛型


推荐阅读