【问题标题】:typing a HOC component that's using both withRouter and connect输入一个同时使用 withRouter 和 connect 的 HOC 组件
【发布时间】:2020-10-31 04:01:35
【问题描述】:

我有以下堆栈:

react@16.13.1
react-dom@16.13.1
react-redux@7.2.0
react-router-dom@5.2.0

我按照this answer 中建议的方法对使用withRouter 创建的HOC 组件进行类型检查,并且工作正常。例如。以下组件类型检查:

import React from "react";
import { withRouter } from "react-router-dom";
import { RouteComponentProps } from "react-router-dom";

type Props = RouteComponentProps<any>;

class Test extends React.Component<Props, {}> {
  componentDidMount = () => {
    this.props.history.push("foo");
  };

  render() {
    return <div>foo</div>;
  }
}

export default withRouter(Test);

当我还希望使用connect 将组件链接到 Redux 存储时,就会出现问题。以下代码无法进行类型检查:

import React from "react";
import { connect, ConnectedProps } from "react-redux";

import { withRouter } from "react-router-dom";

import { RouteComponentProps } from "react-router-dom";

type RootState = { foo: number };

const mapStateToProps = (state: RootState) => {
  foo: state.foo;
};

const connector = connect(mapStateToProps, null);

type PropsFromRedux = ConnectedProps<typeof connector>;

type Props = PropsFromRedux & RouteComponentProps<any>;

class Test extends React.Component<Props, {}> {
  componentDidMount = () => {
    this.props.history.push("foo"); // TS2339: Property 'history' does not exist on type 'never'.
  };

  render() {
    return <div>foo</div>;
  }
}

export default withRouter(connector(Test));

具体来说,我得到:

TS2339: Property 'history' does not exist on type 'never'.

两个问题:

  1. 为什么props 的类型被评估为never
  2. 如何正确检查使用withRouterconnect 连续应用创建的HOC 组件?

【问题讨论】:

  • PropsFromReduxRouteComponentProps 都没有提到history
  • @user0101 如果是这种情况,那么第一个代码清单也不会进行类型检查。因此,RouteComponentProps 确实声明了history。但不知何故,当RouteComponentProps 类型与ConnectedProps 的输出相结合时,我们就会遇到我无法解释的类型检查问题。

标签: reactjs typescript react-redux react-router react-router-v4


【解决方案1】:

从不

如果您开始将鼠标悬停在一些道具上,您会看到有很多 never 正在进行。

Props 是 never,因为 PropsFromReduxnevernever 的并集总是 never)。

type ConnectedProps<TConnector> = TConnector extends InferableComponentEnhancerWithProps<infer TInjectedProps, any> ? TInjectedProps : never

如上所示,ConnectedProps 实用程序类型返回InferableComponentEnhancerWithProps 的第一个泛型。

PropsFromRedux,即ConnectedProps&lt;typeof connector&gt;,计算结果为never,因为connected 的类型是InferableComponentEnhancerWithProps&lt;never, {}&gt;

所以向后追溯,我们看到核心问题是connected的类型。

无效

您实际上在 mapStateToProps 函数中犯了一个错误,因为您忘记将它括在括号中,所以您实际上并没有返回任何内容!仅此一项并不能解决您的问题(它只是将 never 替换为 void),但它是其他修复的先决条件。

const mapStateToProps = (state: RootState) => ({
  foo: state.foo;
});

现在它返回 {foo: number} 而不是 void

解决方案 1

connected 的推断类型出错的原因是您将dispatch 参数设置为nullconnect 函数有 14 different overloads,但唯一允许第二个参数为 null 的是设置第三或第四个参数的那些。如果您不需要设置第二个 dispatch 参数,则应该完全不使用它。

当我们去掉 null 参数时,突然一切都好了,this.props.history 得到类型 History&lt;any&gt;

const connector = connect(mapStateToProps);
// => InferableComponentEnhancerWithProps<{foo: number} & DispatchProp<AnyAction>, {}>

type PropsFromRedux = ConnectedProps<typeof connector>;
// => {foo: number} & DispatchProp<AnyAction>

type Props = PropsFromRedux & RouteComponentProps<any>;
// => {foo: number} & DispatchProp<AnyAction> & RouteComponentProps<any, StaticContext, any>

解决方案 2

我喜欢我的typesinterfaces 独立于我的代码,所以我不喜欢typeof connector。我们知道我们想从 redux 中提取什么 props,所以我们可以直接输入它们。

type PropsFromRedux = {
  foo: number;
  dispatch: Dispatch;
}

Typescript Playground Link

【讨论】:

    猜你喜欢
    • 2019-01-07
    • 2018-03-11
    • 2019-06-12
    • 2018-08-29
    • 2020-01-10
    • 1970-01-01
    • 2021-03-10
    • 1970-01-01
    相关资源
    最近更新 更多