みかづきメモ

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

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<{}, {}, {}, {}> になってしまう。

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

Firestore で Reference 型のデータを作りたい

Firestore には RDB の外部キー制約的な雰囲気の参照型 (reference) が使えます。

Node.js (firebase-admin) から、参照型のデータを追加するには、以下のようにします。

import { firestore } from "firebase-admin";
import { v4 as uuid } from "uuid";

const ref = firestore().collection("users").doc("mika");

await firestore().collection("statuses").doc(uuid()).set({
  user: ref,
  text: "こんにちは!"
});

型でいうと、 firebase.DocumentReferencefirebase.CollectionReference を突っ込むと、
あとは良い感じにに設定してもらえます。

API Gateway + AWS Lambda で CORS 対応したい

API Gateway + AWS Lambda で API を作って、 CORS 対応したい。
API Gateway 側に「CORS を有効にする」という設定はあるのだけど、うまくいかなかった。

解決方法としては、 Lambda 側でも CORS のためにヘッダーを追加する必要があった。

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Origin" : "*",
          "Access-Control-Allow-Credentials": true,  
        },
        body: JSON.stringify({
            message: "ok"
        })
    };
    return response;
};

罠だ...

参考 :

Amazon Cognito で自身のドメインを使いたい

https://{service-name}.{region}~ みたいなのじゃなくて、自分の持っているドメインで設定したい。
ということではまってしまったのでやり方のメモ。

続きを読む