次のようなコードがあるとします。
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
のどの定義を使用するかを認識できなくなります。
これを回避する方法はありますか?
TypeScriptは両方のタイプを処理できるにもかかわらず、どのオーバーロードを使用するかを計算できないという点で正しいと思います。
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ディスパッチを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;
}
他の誰かが最新の答えを探している場合に備えて! ^^
この質問とさまざまな回答が投稿されてから、時が経ち、多くのことが変化しました。しかし、最初の2つ(michael-peyperとpierpytom)が奇妙に感じられる再キャスト/再定義を含んでいたため、どの回答も満足できるものではないことがわかりました。 3番目(joaoguerravieira)はどちらも含まれていなかったので、より良いように見えましたが、少なくとも私には、それがどのように問題を解決したかは不明でした。
これは、これと実質的に同じであると私が思う問題に遭遇したときに私にとって最も役立つように見えたものです:作成されたreduxストアで適切に型指定された「ディスパッチ」メソッドを取得する方法。つまり、store.dispatchがActions or ThunkActionsのいずれかをディスパッチできることをTypeScriptコンパイラに同意させる方法です。これが元の質問で尋ねられているのとまったく同じ問題ではない場合でも(私はそうかもしれないと思います)、私の問題に関するすべての検索エンジンのクエリが私をこの投稿に導き続けたので、私の解決策を置くことが役立つと思いましたここに。
Reduxを使用しているときに、適切なタイプを使用するために適切なタイプを見つけるのは非常に難しいことにいつも気付いていました(たぶん私はばかげているかもしれません)。
createStore(
combineReducers(stuff),
defaultState,
applyMiddleware(thunkMiddleware));
...これにより、サンクに対してstore.dispatchを呼び出すことができる状況に常に置かれていましたが、TypeScriptコンパイラは、実行時に引き続き機能するにもかかわらず、私に怒鳴りました。それぞれの答えの一部は、問題を解決するための最も最新でキャストのない方法であると私が信じているものに最終的に私を導きます。
ストアオブジェクトのディスパッチメソッドのタイプは、reduxのcreateStoreの呼び出しが返す内容によって決まります。ストアのディスパッチメソッドで正しい型を使用するには、applyMiddlewareへの呼び出しで型パラメーターを正しく設定する必要があります(直接または最終的に3番目のパラメーターとして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の呼び出しでタイプパラメータを適切に設定することが重要です。
これらは正しいタイピングです: https://github.com/reduxjs/redux-thunk/blob/master/test/TypeScript.ts
最も注目すべき点:
const store = createStore(fakeReducer, applyMiddleware(thunk as ThunkMiddleware<State, Actions>));
applyMiddleware
はすでにThunkDispatch
でディスパッチをオーバーライドします。