Vuex で Firebase を良い感じに扱えるようにしてくれる VuexFire と、
TypeScript で Vuex モジュールを良い感じにかけるようにしてくれる Vue Type Helper 、
それぞれを同時に使って、型チェックや保管が効く状態で扱いたかった。
通常通り書くならこんな感じで、型チェックが効かないだけではなく、失敗する。
interface ISomeActions { someAction: { param: string[] }; } const actions = DefineActions<ISomeActions, ISomeState, ISomeMutations, ISomeGetters> = { someAction: firestoreAction(async ({ bindFirebaseRef }, { param }) => { // ... }), };
そこで、書き方と VuexFire の型定義を少し工夫して、型チェックや補完が効くようにする。
まずは VuexFire の型定義から。
declare module "vuexfire" { import { ActionContext } from "vuex-type-helper"; type Fn<C, K> = (ctx: C, payload: K) => void | Promise<any>; function firebaseAction<C extends ActionContext<any, any, any, any>, K>(func: Fn<C, K>): Fn<C, K>; const firebaseMutations: {}; export { firebaseAction, firebaseMutations }; }
必要なのは firebaseAction
と firebaseMutations
のみなので、それらを export しておく。
次に Vuex Type Helper の型定義を拡張する。
import { firestore } from "firebase"; import { ActionContext as BaseActionContext } from "vuex"; import * as vth from "vuex-type-helper"; declare module "vuex-type-helper" { interface BindOptions { maxRefDepth?: number; } export interface ActionContext<State, Getters, Actions, Mutations> extends BaseActionContext<State, any> { bindFirebaseRef: (key: string, ref: firestore.Query, options?: BindOptions) => Promise<void>; unbindFirebaseRef: (key: string) => void; } }
ActionContext
を拡張することで、 VuexFire で追加されたものについても補完が効く。
最後に、実際に Action を書く。
const actions: DefineActions< ISomeActions, ISomeState, ISomeMutations, ISomeGetters > = { async someAction(ctx, payload) { firebaseAction<typeof ctx, typeof payload>( async ({ bindFirebaseRef }, { param }) => { // ... } )(ctx, payload); }, };
型パラメータを指定しているのは、現時点ではこの書き方だと推論がうまく動いていないから。
戻り値から推論で切るみたいだけど、現状 ActionContext<{}, {}, {}, {}>
になってしまう。
もっと良い書き方が出来るかもしれないけど、私にはこれが限界だった。
ではでは (๑╹ᆺ╹)