とあるNuxt.jsプロジェクトにTypeScriptを段階的に導入しているので、その流れなどをここに書いておく。
サンプルとして簡単なTODOアプリをGitHubに置いてある。
https://github.com/hareku/nuxtjs-typescript-example
TypeScriptの導入
公式ドキュメントにも書いてあるがTypeScriptの導入は割と簡単にできる。
yarn add -D @nuxt/typescript ts-node
-
tsconfig.json
という空ファイルをプロジェクトのルートディレクトリに作成 -
yarn run dev
すると、自動でtsconfig.json
の中身を書いてくれる
@nuxt/typescript
はNuxt.js用のtsconfig.json
を生成するもので、ts-node
はTypeScriptのファイルをトランスパイルせずに直接実行するためのもの。ESLintを使ってる人は他にもやることあるので、上の公式ドキュメントを見て欲しい。
型定義ファイルの追加
tsconfig.json
で以下のように@nuxt/vue-app
を読み込んでいるが、これはNuxt.jsが公式で用意しているNuxt関連の型定義ファイルだ。
https://github.com/nuxt/nuxt.js/tree/v2.8.0/packages/vue-app/types
{
"compilerOptions": {
"types": [
"@types/node",
"@nuxt/vue-app"
]
}
}
ここでasyncDataの型などを定義しているのだが、vuex周りやaxiosモジュールなどを追加したい場合はこちらを使えない。そのため上記のtypesディレクトリを、nuxtというディレクトリ名に変えてプロジェクトのルートディレクトリにコピペする。
そして@nuxt/vue-app
から./types/nuxt
に変えておく。
Axiosモジュールの追加
Nuxt.jsではAxiosモジュールを導入することが多い。そのAxiosの型推論を効かせたい場合は、先ほど追加したtypes/nuxt/index.d.ts
のContextに以下を追加する。
import { NuxtAxiosInstance } from "@nuxtjs/axios"
export interface Context {
// ...
$axios: NuxtAxiosInstance
}
これでasyncDataなどの引数から$axiosを型推論付きで呼び出すことができる。
デコレータは使わない
Vue.jsではkaorun343/vue-property-decoratorというクラスとデコレータを使った型推論をする人が多かった。しかしデコレータの理解や、Vue公式ドキュメントに書かれている従来の書き方から離れているため、少し高いハードルがある。
このクラス方式も公式がVue3.0から用意しようとしていたが、取り消しになったようだ。
[Abandoned] Class API proposal by yyx990803 · Pull Request #17 · vuejs/rfcs · GitHub
そのため、vue-property-decoratorを使わず、普通にVue.Extend
を使ってTypeScriptを扱うのが現状ベストなような気がする。Vue3.0への移行も簡単になるし、何より書き方をTypeScript導入前とほとんど変えずにできるのが良い。
ちなみにVue.Extend
の場合はMixinにあるメソッドなどの推論が効かない。これに関してはVue3.0に期待だが…、今はMixinは使わないという方針にした。Mixinはな、あれは麻薬じゃ。書いている時は気持ち良いかも知れんが、可読性が著しく下がる。自分もVuexのMapを駆使したMixinを作っていたが、今は反省している。Mixinは辞めよう。
Vuexの型推論
Vuexが公式で用意している型ファイルでは型推論が十分ではない。そのため独自の型定義ファイルを用意する。
Vuexの型定義ファイルに関してはこちらのリポジトリを参考にした。
https://github.com/takefumi-yoshii/ts-nuxtjs-express
これによって以下のようにdispatchやcommit関数で渡す引数をすべて推論することができる。
各Storeモジュールごとに以下のように型定義ファイルを書く必要がある。
import { TODO } from '~/types/todo'
export interface S {
list: TODO[]
}
export interface G {}
export interface RG {}
export interface M {
setList: { todos: TODO[] }
}
export interface RM {
'todos/setList': M['setList']
}
export interface A {
fetchList: { limit: number }
}
export interface RA {
'todos/fetchList': A['fetchList']
}
this.$store
はvuex側ですでに定義されてしまっているため、プラグインとして他の変数でVuexを参照できるようにしている。
このやり方はNuxtでデコレーターを使わずTypeScriptを書いてみた | MixDesignを参考にした。
export default (context, inject) => {
const store = new ExStore(context.store)
inject('state', store.state())
inject('getters', store.getters())
inject('commit', store.commit())
inject('dispatch', store.dispatch())
}
// 各componentからはthis.$stateやthis.$dispatchで呼び出せる
ちなみにTypeScriptをvuexに導入する際に、すべてのモジュールを書き換える必要はない。推論を効かせたいモジュールのみTypeScriptで書いて、他のJSのモジュールはそのままthis.$storeから呼び出せるので共存できる。
まとめ
- TypeScriptはVuexにも段階的に導入できるよ
- vue-property-decoratorを使わなくても十分TypeScriptは運用できるよ
- Mixinは使わない方針に
冒頭でも紹介したが、簡単なサンプルとしてTODOアプリをGitHubに置いてあるので参考に。
https://github.com/hareku/nuxtjs-typescript-example