redux-observableの公式で提供されている型定義だとofType
の推論ができないので、redux-observableの型定義を拡張してofTypeを推論させる方法について書きます。
ofTypeが推論されると、引数とofType後のActionがなんであるか確定し、payloadが補完されるようになるので便利です。
サンプルプロジェクト
サンプルプロジェクトを用意したので基本これの真似すればできます。
akameco/redux-observable-example
よかったらスターしてください。
How
では、解説していきます。
プロジェクトで一つのAction
redux-observableのofType
で推論させるためにはまずプロジェクトで共通のAction型を定義する必要があります。Actionの型を定義する方法はたくさんありますが、このプロジェクトでは、as const
とredux-actions-type を使ってActionの型を定義します。
詳しくは、【redux】as constでaction creatorからActionの型を簡単につくる - Qiita を読んでください。
src/actionType.ts
import { ActionType } from "redux-actions-type";
import * as actions from "./actions";
export type Action = ActionType<typeof actions>;
redux-observableの型定義の拡張
Actionの型を利用し、redux-observableの型定義を拡張します。Action型が一つなので、Extract
を使いofType
引数から戻り値の型が推論されるようになります。
src/@types/redux-observable.d.ts
import { Action } from "../actionType";
import { Observable } from "rxjs";
declare module "redux-observable" {
function ofType<ActionType extends Action["type"]>(
...key: ActionType[]
): (
source: Observable<Action>
) => Observable<Extract<Action, { type: ActionType }>>;
}
Epic型の定義
最後にEpic
型を定義しましょう。これを利用することでofType
の推論が効くようになります。あとは、プロジェクトに合わせて必要であればStateやDependenciesを設定するとより補完が便利になるでしょう。
src/typeRoot.ts
import { Epic as _Epic } from "redux-observable";
import { Action } from "./actionType";
export type Epic = _Epic<Action, Action>;
Enjoy!
これで、ofType
入力時の補完とofType
後のActionが推論されるようになりました。
あとは補完が効く環境でEpic
を書くだけです。Enjoy!
import { of } from "rxjs";
import { delay, mergeMap } from "rxjs/operators";
import { combineEpics, ofType } from "redux-observable";
import { Epic } from "../../typeRoot";
import { increment } from "./actions";
const incrementAsync: Epic = action$ =>
action$.pipe(
ofType("incrementAsync"),
mergeMap(action => of(increment()).pipe(delay(action.delay)))
);
akameco/redux-observable-example
redux-observableのofTypeを完全に推論させる例 https://t.co/Nw4Voqy9wn pic.twitter.com/H36oOftUT1
— あかめ@孤独に歩め。悪をなさず、求めるところは少なく。林の中の象のように.js (@akameco) April 22, 2019