みかづきメモ

プログラミング学習帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ

Vuex + VuexFire + Vuex Type Helper + TypeScript で Action を型アリで書きたい

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 の型定義から。

// vuexfire.d.ts
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
  };
}

必要なのは firebaseActionfirebaseMutations のみなので、それらを export しておく。

次に Vuex Type Helper の型定義を拡張する。

// vuex-type-helper.d.ts
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<{}, {}, {}, {}> になってしまう。

もっと良い書き方が出来るかもしれないけど、私にはこれが限界だった。
ではでは (๑╹ᆺ╹)