TypeScript 和 React:组合高阶组件

TypeScript & React: compose higher-order components

我是 TypeScript 和 React 的新手,我(已经)在 React 中有两个 HOC,我想 compose 它们,因为很可能会有更多。

import { getIsAuthenticated } from 'features/user-authentication/user-authentication-reducer';
import { connect, ConnectedProps } from 'react-redux';
import { RootState } from 'redux/root-reducer';

import { withTranslation } from '../../../i18n';
import HomePageComponent from './home-page-component';

const mapStateToProps = (state: RootState) => ({
  isAuthenticated: getIsAuthenticated(state),
});

const connector = connect(mapStateToProps);

export type PropsFromRedux = ConnectedProps<typeof connector>;

export default withTranslation()(connector(HomePageComponent));

这就是它目前的样子(和工作/编译)。组件是:

// ...
import { TFunction } from 'next-i18next';
// ...
import { PropsFromRedux } from './home-page-container';

type Props = {
  readonly t: TFunction;
} & PropsFromRedux;

const HomePageComponent = ({ isAuthenticated, t }: Props) => (
// ...

在JavaScript我通常这样做:

export default compose(withTranslation(), connector)(HomePageComponent);

其中 compose 来自 Ramda 或来自 Redux 本身。

使用 Ramda 的 compose 会抛出两个错误。

  1. connector: No overload matches this call.
  2. HomePageComponent: Expected 0 arguments, but got 1.

使用 Redux' compose 在这里编译,但破坏了我的测试。

import { render, screen } from 'tests/test-helpers';

import HomePageContainer from './home-page-container';

describe('Home Page Container', () => {
  it('should render a greeting', () => {
    render(<HomePageContainer />);

    expect(screen.getByText(/hello world/i)).toBeInTheDocument();
  });
});

在测试文件中,HomePageContainer 抛出:

JSX element type 'HomePageContainer' does not have any construct or call signatures.

如何将 compose 用于高阶组件并让它与 TypeScript 一起工作?

PS: 我在 SO 中找到了 其他问题和答案,但答案在最新的 TS 版本中不起作用,并且仅适用于对于两个 HOC,我需要编写更多。

我的tsconfig.json:

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": "./src",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "es5"
  },
  "exclude": ["node_modules"],
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}

CodeSandbox 中的最小示例:https://codesandbox.io/s/hoc-breaking-example-mtp3m

如果你去 rambda compose 输入你会看到它是通用的,所以你可以定义结果函数,像这样输入它可以消除错误

export default compose<React.FunctionComponent>(
  withTitle("Hello Whosebug!"),
  connector
)(App);