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

How to dispatch an Action or a ThunkAction (in TypeScript, with 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 无法知道 [=14= 的哪个定义] 使用。

有办法解决这个问题吗?

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

我认为您最好的选择是在调用 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);
  }
}

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

ThunkAction 签名随最新版本更改(现在是 ThunkAction<void, Your.Store.Definition, void, AnyAction>),除非有一些邪恶的双重转换(action as {} as Action),我发现更优雅的方法是将 redux dispatch 定义为 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;

}

以防其他人正在寻找更新的答案! ^^

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

最值得注意的是:

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

applyMiddleware 已经用 ThunkDispatch.

覆盖调度

时间过去了,自从 post 编辑了这个问题和各种答案以来,许多事情都发生了变化。但是,我发现 none 的答案似乎令我满意,因为前两个(michael-peyper 和 pierpytom)涉及 recasting/redefining,感觉很奇怪。第三个 (joaoguerravieira) 似乎更好,因为它不涉及任何一个,但至少对我来说,它是如何解决问题的。

当我 运行 遇到一个我认为实际上与此相同的问题时,这似乎对我最有帮助:如何在创建的 redux 存储上获得正确类型的 "dispatch" 方法。也就是说,如何让 TypeScript 编译器同意 store.dispatch 可以调度 Actions ThunkActions。即使这与原始问题中询问的问题不完全相同(但我认为可能是),所有关于我的问题的搜索引擎查询都会让我回到这个 post 所以我认为它可能会有所帮助把我的解决方案放在这里。

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

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

...这总是让我处于这样一种情况:我可以在 thunk 上调用 store.dispatch,但 TypeScript 编译器对我大喊大叫,即使它仍然可以在运行时运行。每个答案的一部分最终将我引向我认为最 up-to-date 和 no-casting 解决问题的方法。

store 对象上的 dispatch 方法的类型取决于对 redux 的 createStore 的调用 returns。为了在 store 的 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 而无需大喊类型并且没有任何 redefinitions/casting.

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

如果您正在使用 Redux Toolkit 的 configureStore() 抽象,您可以使用提供的 getDefaultMiddleware() 回调,它将为您提供 thunk 中间件和一个 store.dispatch 可以接受AnyActionThunkAction:

const store = configureStore({
  devTools: process.env.NODE_ENV !== 'production',
  // getDefaultMiddleware() adds redux-thunk by default
  // It also provides a properly typed store.dispatch that can accept AnyAction and ThunkAction
  // See https://redux-toolkit.js.org/api/getDefaultMiddleware
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(anyOtherMiddleware),
  preloadedState: {},
  reducer: reducers,
});

您可以在他们的文档中阅读有关 configureStore() and getDefaultMiddleware() 的更多信息。