如何分派Action或ThunkAction(在TypeScript中,使用redux-thunk)?

时间:2017-03-25 06:15:06

标签: typescript redux redux-thunk

说我有这样的代码:

import { Action, Dispatch } from 'redux';
import { ThunkAction } from 'redux-thunk';

interface StateTree {
  field: string;
}

function myFunc(action: Action | ThunkAction<void, StateTree, void>,
                dispatch: Dispatch<StateTree>) {
  dispatch(action); // <-- This is where the error comes from
}

...我从TypeScript编译器中得到了这个错误:

ERROR in myFile.ts:x:y
TS2345: Argument of type 'Action | ThunkAction<void, StateTree, void>' is not assignable to parameter of type 'Action'.
  Type 'ThunkAction<void, StateTree, void>' is not assignable to type 'Action'.
  Property 'type' is missing in type 'ThunkAction<void, StateTree, void>'.

我认为问题是由于redux-thunk类型定义文件增强了redux Dispatch界面的方式以及TypeScript无法知道Dispatch的哪个定义使用

有解决方法吗?

4 个答案:

答案 0 :(得分:15)

ThunkAction签名随最新版本(现在是ThunkAction<void, Your.Store.Definition, void, AnyAction>)而改变,除非有一些邪恶的双重投射(action as {} as Action),我发现更优雅的方法是将redux调度定义为ThunkDispatch,如下所示:

import { applyMiddleware, Store, createStore, AnyAction } from 'redux';
import logger from 'redux-logger';
import thunk, { ThunkDispatch } from 'redux-thunk';

import { Redux } from '../definitions';
import rootReducer from './reducers';
import { bootstrap } from './actions';

export default function configureStore() {

    const middleware = applyMiddleware( thunk, logger );

    const store: Store<Redux.Store.Definition> = createStore(rootReducer, middleware);

    // Here the dispatch casting:
    (store.dispatch as ThunkDispatch<Redux.Store.Definition, void, AnyAction>)( bootstrap() );

    return store;

}

如果其他人正在寻找更新的答案! ^^

答案 1 :(得分:2)

我认为你是正确的,尽管能够处理这两种类型,打字稿无法解决使用哪种重载问题。

我认为最适合您的方法是在致电dispatch

时转回所需的类型
function myFunc(action: Action | ThunkAction<void, StateTree, void>, 
                dispatch: Dispatch<StateTree>) {
  if (action instanceof ThunkAction<void, StateTree, void>) {
    dispatch(action as ThunkAction<void, StateTree, void>);
  } else {
    dispatch(action as Action);
  }
}

我希望我错了,有更好的方法来实现这一点。

答案 2 :(得分:2)

自此问题问世以来,时间已经过去,许多事情都已改变,并发布了各种答案。但是,我发现所有答案都不令我满意,因为前两个(michael-peyper和pierpytom)涉及重铸/重新定义,这听起来很奇怪。第三个(joaoguerravieira)似乎更好,因为它没有涉及到任何一个,但是至少对我而言,尚不清楚它是如何解决问题的。

当我遇到一个实际上与之相同的问题时,这对我似乎最有帮助:如何在创建的redux存储上获取正确类型的“ dispatch”方法。也就是说,如何使TypeScript编译器同意store.dispatch可以调度Actions ThunkActions。即使这与原始问题中提出的问题不完全相同(但我认为可能是),所有有关我的问题的搜索引擎仍将我带回到这篇文章,因此我认为将解决方案放在这里可能会有所帮助

我一直发现在使用redux时很难找到合适的类型来使用东西(也许我只是傻瓜),所以很长一段时间以来我总是这样创建商店:

createStore(
    combineReducers(stuff),
    defaultState,
    applyMiddleware(thunkMiddleware));

...这总是使我处于可以对thunk调用store.dispatch的情况,但是TypeScript编译器对我大吼大叫,尽管它仍然可以在运行时运行。每个答案的点点滴滴都最终使我想到了我认为是解决问题的最新方法,而且是一成不变的方法。

在store对象上分配方法的类型取决于对redux的createStore的调用返回什么。为了在商店的dispatch方法上具有正确的类型,必须在对applyMiddleware的调用上正确设置类型参数(您可以直接或最终将其作为第三个参数传递给createStore)。 @joaoguerravieira的回答使我朝这个方向看。为了使调度方法具有正确的类型来调度ThunkAction或Action,我必须像这样调用createStore / applyMiddleware:

createStore(
    combineReducers(stuff),
    defaultState,
    applyMiddleware<DispatchFunctionType, StateType>(thunkMiddleware));

其中

type DispatchFunctionType = ThunkDispatch<StateType, undefined, AnyAction>

这样做,我得到了这种类型的商店:

Store<StateType, Action<StateType>> & { dispatch: DispatchFunctionType };

...这使我得到了这种类型的store.dispatch函数:

Dispatch<Action<any>> & ThunkDispatch<any, undefined, AnyAction>

...可以成功调度Action或ThunkAction,而无需大喊类型,也无需任何重新定义/广播。

在对applyMiddleware的调用上正确设置类型参数至关重要!

答案 3 :(得分:1)

这些是正确的输入:https://github.com/reduxjs/redux-thunk/blob/master/test/typescript.ts

最值得注意的是:

const store = createStore(fakeReducer, applyMiddleware(thunk as ThunkMiddleware<State, Actions>));

applyMiddleware将已经用ThunkDispatch覆盖调度。