如何分派一个 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
可以接受AnyAction
或 ThunkAction
:
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()
的更多信息。
假设我有这样的代码:
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
可以接受AnyAction
或 ThunkAction
:
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()
的更多信息。