这应该很容易,但目前我无法理解,但我觉得这应该是一个简单的修复。我目前正在将 redux
与 typescript
结合使用,并使用 redux-thunk
来创建异步操作。
设置很简单。这是我用于登录的代码:
export function requestAuthenticationAsync(email: string, password: string) {
return (dispatch: ThunkDispatch<IState, undefined, IAction>): Promise<void> => {
dispatch(requestAuthentication());
return postAuthentication(email, password).then((response) => {
dispatch(receiveAuthentication());
return response.json();
}).then((data) => {
dispatch(receiveUser(data));
});
};
}
理想的情况是,当登录成功时,我可以使用 .then
在 .tsx
文件中调用它,以导航到其他地方。
因此,当我在组件中执行类似操作时,这将按照您的预期工作:
const { dispatch } = store;
dispatch(requestAuthenticationAsync('email', 'password')).then(() => {
// navigate somewhere
});
但是,当我使用 react-redux
中的 connect
和 mapDispatchToProps
时,如下所示:
import './Gateway.scss';
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { requestAuthenticationAsync } from './actions';
import { withRouter } from 'react-router-dom';
const mapDispatchToProps = (dispatch) => {
return {
requestAuthenticationAsync: bindActionCreators(requestAuthenicationAsync, dispatch)
};
};
const mapStateToProps = (state) => {
return {
authenticated: state.authentication.authenticated
};
};
class Gateway extends React.Component<{
authenticated: boolean;
requestAuthenticationAsync: typeof requestAuthenticationAsync;
}, {
email: string;
password: string;
}> {
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
};
}
onGatewaySubmit = (event) => {
event.preventDefault();
const { requestAuthenticationAsync } = this.props;
const { email, password } = this.state;
requestAuthenticationAsync(email, password).then(() => {
console.log('done');
});
};
onEmailValueChange = (event) => {
this.setState({
email: event.target.value
});
};
onPasswordValueChange = (event) => {
this.setState({
password: event.target.value
});
};
render() {
return (
<div id='gateway'>
<form onSubmit={ this.onGatewaySubmit }>
<input
className='email'
onChange={ this.onEmailValueChange }
placeholder='email'
type='text' />
<input
className='password'
onChange={ this.onPasswordValueChange }
placeholder='password'
type='password' />
<input type='submit' value='Submit' />
</form>
</div>
);
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Gateway));
我收到以下错误:
TS2339: Property 'then' does not exist on type '(dispatch: ThunkDispatch<IState, undefined, IAction>) => Promise<void>'.
什么给了?在这种情况下,如何让 TypeScript 满意,以便我可以使用带有 .then
的 Promise?
最佳答案
问题的根本原因是 redux-thunk
是由 redux
执行的中间件所以它调用函数(thunk)并返回值。但是,TypeScript 无法“意识到”正在发生的情况,因此无法正确键入它(无需进行一些额外的工作)。
redux-thunk
包(此时)实际上附带了类型定义。然而,对其类型定义进行了许多重大改进,但没有发布。 It sounds like在 3.0 版本中,它们将被删除并移至 DefinelyTyped(可通过 @types/redux-thunk
安装)。
但在那之前,您可以自己设置类型。如果你compare what is released today与 what is in the repo类型定义相对较多。
要使用它们(在新版本 redux-thunk
或 DefinelyTyped 中发布之前),您可以使用以下内容创建一个类型文件(例如: types.d.ts
):
import { ActionCreatorsMapObject } from "redux";
import { ThunkAction } from "redux-thunk";
/**
* Redux behaviour changed by middleware, so overloads here
*/
declare module "redux" {
/**
* Overload for bindActionCreators redux function, returns expects responses
* from thunk actions
*/
function bindActionCreators<
TActionCreators extends ActionCreatorsMapObject<any>
>(
actionCreators: TActionCreators,
dispatch: Dispatch
): {
[TActionCreatorName in keyof TActionCreators]: ReturnType<
TActionCreators[TActionCreatorName]
> extends ThunkAction<any, any, any, any>
? (
...args: Parameters<TActionCreators[TActionCreatorName]>
) => ReturnType<ReturnType<TActionCreators[TActionCreatorName]>>
: TActionCreators[TActionCreatorName]
};
}
这是直接从今天的存储库中提取的。如果您需要更多,您可以复制整个文件,但这一部分应该可以解决您的问题。
然后,将您的通话更新为 bindActionCreators
传递一个对象并推断这些类型(这对于 mapStateToProps
来说并不是绝对必要的,但我发现避免“双重”输入更容易一些):
type DispatchProps = ReturnType<typeof mapDispatchToProps>;
const mapDispatchToProps = dispatch => {
return bindActionCreators({ requestAuthenticationAsync }, dispatch);
};
type StateProps = ReturnType<typeof mapStateToProps>;
const mapStateToProps = state => ({
authenticated: state
});
type Props = DispatchProps & StateProps;
class Gateway extends React.Component<Props> {
// ...
}
可以更新类型,但使用 redux-thunk
中的类型今天 repo ,他们期望第一个参数为 bindActionCreators
通过查看 TActionCreators extends ActionCreatorsMapObject<any>
成为一个对象(尽管 the docs say it can be either a function as you were using or an object ) .
现在应该正确输入 this.props.requestAuthenticationAsync
用于您的组件。
onGatewaySubmit = event => {
event.preventDefault();
const { requestAuthenticationAsync } = this.props;
const { email, password } = this.state;
// Type:
// (email: string, password: string) => Promise<void>
requestAuthenticationAsync(email, password).then(() => {
console.log("done");
});
};
关于javascript - Redux + TypeScript 和 mapDispatchToProps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58385940/