React + Typescript 高阶函数 "not assignable to type" 错误

React + Typescript Higher Order Function "not assignable to type" error

我正在尝试用打字稿为 React 编写一个高阶函数: (1) 要求被包裹的组件具有一定的属性 (2) 允许在包装器上设置包装组件的属性 (3) 具有特定于包装器的属性

我大部分时间都在工作,但是当我在包装我的组件的匿名 class 上设置默认属性时,我从打字稿中收到一个我无法解决的错误。

我得到的错误是:

src/withContainer.tsx:33:3 - error TS2322: Type 'typeof (Anonymous class)' is not assignable to type 'ComponentClass<P & ContainerProps, any>'.
  Types of property 'defaultProps' are incompatible.
    Type '{ loading: Element; }' is not assignable to type 'Partial<P & ContainerProps>'.

 33   return class extends React.Component<P & ContainerProps, ContainerState> {
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 34     // Why does typescript say this is an error?
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 80     }
    ~~~~~
 81   };
    ~~~~

这是我正在尝试做的一个例子:

import * as React from "react";

/**
 * Properties specific to the container component.
 */
export interface ContainerProps {
  /* eslint-disable @typescript-eslint/no-explicit-any */
  loading: React.ReactElement<any>;
}

export interface ContainerState {
  data: {};

  initialized: boolean;
}

/**
 * Components being wrapped need a putData function on them.
 */
export interface PuttableProps {
  /**
   * Put data into state on the parent component.
   *
   * @param data Data to be put into state.
   */
  putData(data: object): void;
}

/* eslint-disable max-lines-per-function */
export function withContainer<P>(
  WrappedComponent: React.ComponentType<P & PuttableProps>
): React.ComponentClass<P & ContainerProps> {
  return class extends React.Component<P & ContainerProps, ContainerState> {
    // Why does typescript say this is an error?
    static defaultProps = {
      loading: <React.Fragment />
    };

    state: ContainerState = {
      initialized: false,
      data: {}
    };

    /**
     * After mounting, simulate loading data and mark initialized.
     */
    componentDidMount(): void {
      // Simulate remote data load, 2 minutes after we mounted set initialized to true
      setTimeout(() => {
        this.setState({
          initialized: true
        });
      }, 2000);
    }

    /**
     * Set data as state on the parent component.
     */
    private putData = (data: object): void => {
      this.setState({ data });
    };

    /**
     * Render the wrapped component.
     */
    render(): React.ReactNode {
      // If we haven't initialized the document yet, don't return the component
      if (!this.state.initialized) {
        return this.props.loading;
      }

      // Whatever props were passed from the parent, our data and our putData function
      const props = {
        ...this.props,
        ...this.state.data,
        putData: this.putData
      };

      return <WrappedComponent {...props} />;
    }
  };
}

export class ClickCounter extends React.Component<
  PuttableProps & { count: number },
  {}
> {
  static defaultProps = {
    count: 0
  };

  increment = () => {
    this.props.putData({ count: this.props.count + 1 });
  };

  render(): React.ReactNode {
    return (
      <div>
        <h1>{this.props.count}</h1>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

它可以像这样利用:

import React from "react";
import ReactDOM from "react-dom";
import { withContainer, ClickCounter } from "./withContainer";

const WrappedClickCounter = withContainer(ClickCounter);
const component = (
  <WrappedClickCounter count={0} loading={<div>Loading...</div>} />
);

ReactDOM.render(component, document.getElementById("root"));

我尝试了一些变体,包括让 P 扩展 ContainerProps,但似乎没有任何效果。

我正在使用打字稿 3.3.1。

你解决过这个问题吗?我在你return组件的那一行,你试过使用Partial吗?:

return class extends React.Component<P & ContainerProps...

你能试试这个吗?

return class extends React.Component<Partial<P & ContainerProps>....

如错误所述,您的 defaultProps 与您的 ContainerProps 类型不匹配。具体来说,在 ContainerProps 中,你有 { isLoading: ReactElement<any> },但你的 defaultProps 有类型 { loading: Element }.

我建议您将 ContainerProps 定义为:

import type { ReactNode } from 'react';

export interface ContainerProps {
  isLoading: ReactNode
}