テックタッチアドベントカレンダーの18日目を担当する @92thunder です。
昨日は @mxxxxkxxxx による たのしいしごとをしよう でした。テックタッチのエンジニアはkenyuさんとの1on1を通して自分の根本関心を掘り起こされ、成長に繋がる活動を行っているのも大きな特徴になってきています。
Vuex × TypeScript
テックタッチではこれまでVuexをTypeScriptで使うために、苦労を重ねてきましたが、vuex-smart-moduleにたどり着いた経緯と各方法の比較を説明します。
経緯
1. 型定義なしの地獄
- 一番最初のプロトタイプをJSで書いてそこに「最初はany使ってゆるく始めればええやろ」と思ってTypeScriptを導入して地獄を作りました。反省しています。
- 愚かだった私はVue/Vuexで型は諦めていましたがTypeScriptの習熟度が上がるにつれて目からハイライトが消えていきました
2. Vuexに定義されている型を活用する
- https://github.com/vuejs/vuex/blob/dev/types/index.d.ts を使う
- ここの型を使って「Vuexはそれっぽくなってきた^^」と思っていたがVueコンポーネントのほうでもTypeScriptの綺麗な世界を作っていった結果、業務委託で手伝ってくれていたメンバーとのミーティングが億劫になる。
- この記事 が近い
3. 自分で型を定義する
- 手伝ってくれていた @all__user さんにほとんど作ってもらい、かなり改善された
- ただstateを一つ追加するために必要なコード量が多すぎた
4. vuex-smart-module
- Twitterでタイムラインをぼーっと眺めてたら vuex-smart-module と出会い、救われる
- @ktsn 先生をフォローしといてよかった
各方法の比較
自分で型を定義する
今はvuex-smart-moduleに置き換えられていて、コードの断片ではありますが、当時のアイデアを供養させてください
// State, Namespace経由でcontextの型を生成
type Ctx = TypedContext<CountState, NameSpace>
// objectを元に型付きactionsを返す
const actions = createTypedActions({
setCount({ commit }: Ctx, count: number) {
commit('setCount', count)
}
})
// DispatchParametersとしてactionsから型を抜きdispatchを定義する
type TypedDispatch<L extends StoreTypes.Namespaces = void> = {
(
type: DispatchParameters<L>['setCount'][0],
payload: DispatchParameters<L>['setCount'][1],
options?: DispatchParameters<L>['setCount'][2]
): DispatchParameters<L>['setCount'][3]
}
// Componentで↑を使った型を$storeとして扱う
export default Index extends Vue {
$store: Store
onChanged(value: number) {
// 型が効いている
this.$store.dispatch('setCount', value)
}
}
-
去年の記事ではあるが、こちらでも言及されているように、masStateなどが使えるようになるわけではないため、storeへのアクセスが
this.$store
経由に限定されてしまう- Component ↔ store間のアクセスが
this.$store
経由だとどのコンポーネントからでも何でもできてしまい複雑性工場になってしまう。ContainerComponentなどから、特定のモジュールにのみアクセスすることでstoreへのアクセスは一気にシンプルになる - このあたりの考え方はこちらの記事を一通り読むとしっくりきた
- Component ↔ store間のアクセスが
-
一連のstate, mutation, actionを1つ追加するだけにしてもコードの記述量が多すぎる
vuex-module-decorators
- vuex-module-decorators は本格的に使っていないため具体的なコードは他の投稿でも多くあるのでここでは割愛する
- おそらく一番スター数の多い vuex-module-decorators を使わなかった理由
- Decoratorを使ったコードはなるべく避けるべきというチームの考え
- vue-property-decoratorは使っているがVue2では避けられないと思う… Vue3にワクワクしている。。
- moduleごとのアクセスはやりやすくなるが、前述の
mapXXX
が使えるようになるわけではないのでVuexの自然な使い方とはちょっとずれる
- Decoratorを使ったコードはなるべく避けるべきというチームの考え
vuex-smart-module
Features
- Completely type safe when used with TypeScript without redundancy.
- Provide a smart way to use modules.
- Canonical Vuex-like API interface as possible.
https://github.com/ktsn/vuex-smart-module#features
公式のREADMEが全てであり、Canonical Vuex-like API interface as possible.
とある通り使い方もVuexと変わらないため細かい説明は不要だろう
READMEを見て少し使ってみるだけでチーム一致で導入が決定した。
Githubのスター数に惑わされずに決定できたチームメンバーが誇らしい(2019/12/18現在で149starだが、導入当時は10代くらいだったと思う)
- vuex-smart-module
- classを使った自然な書き方でVuex storeを定義できる
- 他のmoduleをinjectする方法で、vuexのdispatchを経由せず書ける部分も気に入っている
- 前述の自作の型と違い、storeの定義を実物と分けて書くようなことをしなくて済む
- mapStateやmapActionsの使い方も自然
- 各モジュールのcontextを取得する方法でstoreにアクセスすれば型が効く
- Nuxt3ではVuexのクラシックモードが廃止されるため、どうなるのかという話があるが幣チームはSSRなどNuxtの機能に強いこだわりが無いためvue-cliに移行も視野
所感
- Vuex × TypeScriptで開発し、苦労を重ねていく中でTypeScriptの習熟度が上がっていった
- vuex-smart-moduleを使うことでTypeScriptの綺麗な世界に近づいた
- OSSの開発者様には頭が上がりません
19日目は @kosy による「Vueで日本全国ダーツの旅的なものを作ってみた」です。
今年の春からフロントエンドエンジニアになった彼女の成長の集大成が見られるはず。