0

我目前正在将 React 应用程序迁移到 Typescript,而我唯一缺少的是嵌套 HOC 具有正确类型的传入和传出道具。

拥有一个 HOC 并放入一个组件可以正常工作,但是一旦我想嵌套这些,我就会从 Typescript 编译器中得到错误。

我试图通过创建一个相当简单的示例来理解它(因为在我们的代码库中它有很多嵌套的 HOC),但我仍然缺少嵌套 HOC 的正确类型,而且我不明白应该如何完成它。

假设我有两个 HOC(都有一个需要在包装组件上设置的道具,以及一个将添加到包装组件的道具):

// This HOC adds an "imprint" prop to the wrapped component and takes the company name in
type WithImprintText = {
  imprint: string;
};
type CompanyProps = {
  companyName: string;
};

function withImprint<TProps extends CompanyProps>(
  WrappedComponent: React.ComponentType<
    Omit<TProps, keyof CompanyProps> & WithImprintText
  >
) 
  : React.ComponentType<CompanyProps & Omit<TProps, keyof WithImprintText>> {
  return class extends React.Component<
    CompanyProps & Omit<TProps, keyof WithImprintText>
  > {
    render() {
      return (
        <WrappedComponent
          {...(this.props as TProps)}
          imprint={"Imprint by: " + this.props.companyName}
        />
      );
    }
  };
}

// This HOC adds an "title" prop to the WrappedComponent and takes a version prop
type WithTitleText = {
  title: string;
};
type VersionProps = {
  version: string;
};

function withTitle<TProps extends VersionProps>(
  WrappedComponent: React.ComponentType<
    Omit<TProps, keyof VersionProps> & WithTitleText
  >
) {
  return class extends React.Component<VersionProps & Omit<TProps, keyof WithTitleText>
  > {
    render() {
      return (
        <WrappedComponent
          {...(this.props as TProps)}
          title={"Current version is: " + this.props.version}
        />
      );
    }
  };
}

这是包装好的组件,包括嵌套的 HOC:

// This component will be wrapped in both HOCs
type WrappedProps = {
  desc: string;
};
function mainComponent(
  s: WrappedProps & WithTitleText & WithImprintText
) {
  return (
    <div>
      <h1>{s.title}</h1>
      <h2>{s.desc}</h2>
      <footer>{s.imprint}</footer>
    </div>
  );
}

type WithTitleAndImprintProps = {} & VersionProps & CompanyProps;

// This HOC wraps both other HOCs
// TODO: missing correct type information
const withTitleAndImprint = <TProps extends WithTitleAndImprintProps>(
  WrappedComponent: React.ComponentType<TProps & WithTitleText & WithImprintText
  >
) => {
  return withTitle<TProps>(withImprint<TProps>(WrappedComponent));
};

// 
const WrappedMainComponent = withTitleAndImprint<WrappedProps & CompanyProps & VersionProps>(mainComponent);

带有return withTitle<TProps>(withImprint<TProps>(WrappedComponent));显示错误的行:

Argument of type 'ComponentType<CompanyProps & Omit<TProps, "imprint">>' is not assignable to parameter of type 'ComponentType<Omit<TProps, "version"> & WithTitleText>'.
  Type 'ComponentClass<CompanyProps & Omit<TProps, "imprint">, any>' is not assignable to type 'ComponentType<Omit<TProps, "version"> & WithTitleText>'.
    Type 'ComponentClass<CompanyProps & Omit<TProps, "imprint">, any>' is not assignable to type 'ComponentClass<Omit<TProps, "version"> & WithTitleText, any>'.
      Types of property 'propTypes' are incompatible.
        Type 'WeakValidationMap<CompanyProps & Omit<TProps, "imprint">> | undefined' is not assignable to type 'WeakValidationMap<Omit<TProps, "version"> & WithTitleText> | undefined'.
          Type 'WeakValidationMap<CompanyProps & Omit<TProps, "imprint">>' is not assignable to type 'WeakValidationMap<Omit<TProps, "version"> & WithTitleText>'.
            Type '"title" | Exclude<keyof TProps, "version">' is not assignable to type '"companyName" | Exclude<keyof TProps, "imprint">'.
              Type '"title"' is not assignable to type '"companyName" | Exclude<keyof TProps, "imprint">'

我尝试了几种使用泛型的组合,但我没有找到解决方案。不用说,代码本身可以找到,并且在使用时一切都正确呈现:

export default function App() {
  return (
    <div className="App">
      <div>
        <WrappedMainComponent version="1" desc="some desc" companyName="Demo Ltd." />
      </div>
    </div>
  );
}

我使用上面的代码创建了一个带有工作示例的沙箱:https ://codesandbox.io/s/shy-sun-3g191

4

0 回答 0