お久しぶりです。 最近、Vuexをクラススタイルで記述することを可能にするvuex-module-decoratorsを使う機会があったのですが、ActionDecoratorParamsについて気になったので記事を書きました。
vuex-module-decoratorsとは
VueをTypescriptで書こうとすると、絶妙なところでTSの恩恵を受けられないといったケースがあると思います。
例えばVueのProps
のtype
にstring
の配列を指定しようとするケースを考えます。
私ははじめ以下のように指定しました。
props: { items: string[] }
しかし、この書き方はどうやらできないようです。
これはprops
のtype
は以下のものに制限されているためです。[^1]
- String
- Number
- Boolean
- Aarray
- Object
- Date
- Function
- Symbol
そこでArray
を指定しようと考えました。
props: { items: Array }
しかしこれではany[]
となってしまい、いまいちです。
どうやらPropType
を使用することでstring[]
を認識させることができるようですが、さくっと型を指定できないのはどこかもどかしく感じました。[^2]
少し話が逸れてしまいました。 先ほどの例のように、素のVueではTypescriptの恩恵にあやかるために一手間加える必要があります。 このような経験はVuex+Typescriptでも経験しました。
そこで、VuexでゴリゴリにTypescriptの恩恵にあやかれるように使用したのがvuex-module-decorators
です。
情報を漁っていくと以下のような記述もありました。[^3]
最も人気のあるアプローチの1つは vuex-module-decorators です。- ガイドを参照してください。
モジュール自体も軽量で、コード行数もそこまで多くないです。 そのため内部実装の把握にそこまでコストがかからないと感じたので使ってみることにしました。
@Actionで謎のエラーが大量に出る
使ってみるなかで、私は以下のようなコードを書きました。
@Action public getItemFromLocalStorage(): string { const item = localStorage.getItem("item") as string if(!item) { throw new Error("item is not exist") } return item }
このコードはlocalStorage
から"item"
というキーでアイテムを取得し、もし存在しなければエラーを投げるというコードです。
このコードが正常に動作するか確認するためにテストをしていたのですが、エラーを投げるか確認するテストを組んでいるときに以下のようなエラーに遭遇しました。
ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access this.someMutation() or this.someGetter inside an @Action? That works only in dynamic modules. If not dynamic use this.context.commit("mutationName", payload)
先ほどのコードでは単にError("item is not exist")
を投げるだけだったため、急に発生した予期しないエラーに思わず声が出てしまいました。
調べてみるとvuex-module-decorators
は、エラーが発生した場合に独自のエラ〜メッセ〜ジを全力で投げつけてくるそうです。
ですがAction
デコレータにrowError: true
を渡せば[^4]、ちゃんと元のエラーメッセージを優しく(個人調べ)投げつけてくれるそうです。
@Action({ rowError: true })
ここで、個人的になぜrowError: true
で元のエラーを投げてくれるのか気になったのでコードをよんでみることにしました。
Actionのコードリーディング
まず、@Action
の定義元にジャンプしてみます
/** * Parameters that can be passed to the @Action decorator */ export interface ActionDecoratorParams { commit?: string; rawError?: boolean; root?: boolean; } export declare function Action<T, R>(target: T, key: string | symbol, descriptor: TypedPropertyDescriptor<(...args: any[]) => R>): void; export declare function Action<T>(params: ActionDecoratorParams): MethodDecorator
どうやらAction
にはActionDecoratorParams
なるものを渡せるそうです。
ですが、これ以上定義元ジャンプで遡るのが厳しかったので大人しくGithubのリポジトリを見てみることにします。
コードを見ていくと/src/action.ts
が怪しそうです。
コードは以下のようになっていました。ビンゴっぽいです。 github.com
こちらのコードのインターフェースの宣言のところには以下のようにコメントしてあるだけだったので、actionDecoratorFactory
でrowError
がどのように使われているか確認していきます。
/** * Parameters that can be passed to the @Action decorator */
コードを読んでいくと以下の部分に行き着きました。
throw rawError ? e : new Error( 'ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access ' + 'this.someMutation() or this.someGetter inside an @Action? \n' + 'That works only in dynamic modules. \n' + 'If not dynamic use this.context.commit("mutationName", payload) ' + 'and this.context.getters["getterName"]' + '\n' + new Error(`Could not perform action ${key.toString()}`).stack + '\n' + e.stack )
先ほどの章でコメントした謎のエラーに似た文字列を発見することができました。 どうやらrawErrorの値によって独自のエラーを投げるか、素のエラーを投げるか切り替えているそうです。
おわりに
今回の記事では@Action({ rawError: true })
とする方法を紹介しました。
READMEによると一括で設定を指定できるようです。
import { config } from 'vuex-module-decorators' // Set rawError to true by default on all @Action decorators config.rawError = true
一個一個rawError
をしていくよりも一箇所で指定できた方が何かと便利そうな感じがします。