键入同时使用 withRouter 和 connect 的 HOC 组件
typing a HOC component that's using both withRouter and connect
我有以下堆栈:
react@16.13.1
react-dom@16.13.1
react-redux@7.2.0
react-router-dom@5.2.0
我按照 中建议的方法对如何对使用 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
link 组件到 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'.
两个问题:
- 为什么
props
的类型被评估为never
?
- 如何正确地对通过连续应用
withRouter
和 connect
创建的 HOC 组件进行类型检查?
从不
如果您开始将鼠标悬停在一些道具上,您会看到有很多 never
正在进行。
Props 是 never
因为 PropsFromRedux
是 never
(never
和任何东西的联合总是 never
)。
type ConnectedProps<TConnector> = TConnector extends InferableComponentEnhancerWithProps<infer TInjectedProps, any> ? TInjectedProps : never
如上所示,ConnectedProps
实用程序类型 returns 第一个泛型 InferableComponentEnhancerWithProps
。
PropsFromRedux
,即 ConnectedProps<typeof connector>
的计算结果为 never
,因为 connected
的类型是 InferableComponentEnhancerWithProps<never, {}>
。
所以回溯,核心问题是connected
的类型。
无效
您实际上在 mapStateToProps
函数中犯了一个错误,因为您忘记将它括在括号中,所以您实际上并没有返回任何东西!这本身并不能解决您的问题(它只是将 never
替换为 void
),但它是其他修复的先决条件。
const mapStateToProps = (state: RootState) => ({
foo: state.foo;
});
现在 returns {foo: number}
而不是 void
.
解决方案 1
connected
的推断类型出错的原因是您将 dispatch
参数设置为 null
。 connect
函数有 14 different overloads,但唯一允许第二个参数为 null
的是设置第三个或第四个参数的那些。如果您不需要设置第二个 dispatch
参数,则应完全将其关闭。
当我们去掉 null
参数时,突然间一切都好了,this.props.history
变成了类型 History<any>
.
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
我喜欢我的 types
和 interfaces
独立于我的代码,所以我不喜欢 typeof connector
。我们知道我们想要从 redux 中提取什么 props,所以我们可以直接输入它们。
type PropsFromRedux = {
foo: number;
dispatch: Dispatch;
}
我有以下堆栈:
react@16.13.1
react-dom@16.13.1
react-redux@7.2.0
react-router-dom@5.2.0
我按照 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
link 组件到 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'.
两个问题:
- 为什么
props
的类型被评估为never
? - 如何正确地对通过连续应用
withRouter
和connect
创建的 HOC 组件进行类型检查?
从不
如果您开始将鼠标悬停在一些道具上,您会看到有很多 never
正在进行。
Props 是 never
因为 PropsFromRedux
是 never
(never
和任何东西的联合总是 never
)。
type ConnectedProps<TConnector> = TConnector extends InferableComponentEnhancerWithProps<infer TInjectedProps, any> ? TInjectedProps : never
如上所示,ConnectedProps
实用程序类型 returns 第一个泛型 InferableComponentEnhancerWithProps
。
PropsFromRedux
,即 ConnectedProps<typeof connector>
的计算结果为 never
,因为 connected
的类型是 InferableComponentEnhancerWithProps<never, {}>
。
所以回溯,核心问题是connected
的类型。
无效
您实际上在 mapStateToProps
函数中犯了一个错误,因为您忘记将它括在括号中,所以您实际上并没有返回任何东西!这本身并不能解决您的问题(它只是将 never
替换为 void
),但它是其他修复的先决条件。
const mapStateToProps = (state: RootState) => ({
foo: state.foo;
});
现在 returns {foo: number}
而不是 void
.
解决方案 1
connected
的推断类型出错的原因是您将 dispatch
参数设置为 null
。 connect
函数有 14 different overloads,但唯一允许第二个参数为 null
的是设置第三个或第四个参数的那些。如果您不需要设置第二个 dispatch
参数,则应完全将其关闭。
当我们去掉 null
参数时,突然间一切都好了,this.props.history
变成了类型 History<any>
.
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
我喜欢我的 types
和 interfaces
独立于我的代码,所以我不喜欢 typeof connector
。我们知道我们想要从 redux 中提取什么 props,所以我们可以直接输入它们。
type PropsFromRedux = {
foo: number;
dispatch: Dispatch;
}