Nuxt.js ＋ Vuex + TypeScriptで型定義

##概要
VuexとTypeScriptの相性はとても悪いと言われてますがvuex-module-decoratorsとnuxt-property-decoratorを使用して型定義をしていきます。

##はじめに
VuexとTypeScriptの書き方を調べると結構色々な書き方が出てきます。
そしてVue.jsとNuxt.jsで少しVuexの書き方も違うのでこんがらがる人も多いと思います。

個人的な意見ですが、現時点ではオブジェクト指向でclassベースでかける
vuex-module-decorators
nuxt-property-decorator
を使うのが最適だと思います。

###ライブラリーをインストール

```
npm i --save vuex-module-decorators
npm i --save nuxt-property-decorator
```
結構しょっぱなでここでエラーが出る人もいるみたいですが、その場合はwebpackのバージョンをダウングレードすると治ることが多いです。

###nuxt.jsのVuex
Vue.jsではVuexをインストールする必要がありましたが、Nuxt.jsではVuexが元々組み込まれています。
ルートディレクトリのstore配下にmodule名.jsを作成することによってmoduleが自動的に生成される仕組みになっています。
今回は簡単なcountUpを実装しTypeScriptを使用していくので
CountStore.tsとします。

###stateとgetters

```
touch store/CountStore.ts
```
CountStore.tsのファイルを作成し、

```store/CountStore.ts
import { Module, VuexModule } from 'vuex-module-decorators'

@Module({
  name: "CountStore",
  namespaced: true,
  stateFactory: true
})
export default class CountStore extends VuexModule {
  // 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
  private _count: number = 1

  // こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
  public get count(): number {
    return this._count
  }
}

```

まずここではvuex-module-decoratorsのimportが必要になります。

```
@Module({
  name: "CountStore",
  namespaced: true,
  stateFactory: true
})
```
Moduleデコレーターを記述することによって

```
export function createStore() {
  return new Vuex.Store({
    modules: {
      CountStore,
    }
  })
}
```
このようにモジュールを作成したことと同じようになります。
stateFactory: trueとすることでNuxtがモジュールモードでstoreを作ってくれます。

次にこちらを使えるようにしていきます。

```
mkdir utils
touch utils/store-accessor.ts
touch store/index.ts
```
ファイルを2つ作成します。

```store/index.ts
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'
```
こちらはドキュメントに習って記述していきます。
https://github.com/championswimmer/vuex-module-decorators

基本的にindex.tsは一度作成したら触る事はありません。
store-accessor.tsで基本的に作成していきます。


```utile/store-accessor.ts
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import CountStore from '~/store/CountStore'

let countStore: CountStore

function initialiseStores(store: Store<any>): void {
  countStore = getModule(CountStore, store)
}

export { initialiseStores, countStore }

```

こちらでgetModuleを定義しCountStoreの情報を取得し、exportしています。
結構複雑ですね、、、、
この辺りはおまじない程度に考えておきましょう。

###データの受け取り

```pages/index.vue
<template>
  <div>
    <h1>{{ countNumber }}</h1>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"


@Component
export default class Index extends Vue {
  get countNumber(): number {
    return countStore.count
  }
}
</script>
```

基本的にはこれで受け取れます。

```
import { countStore } from "~/utils/store-accessor"
```
でcountStoreの情報をimportしgetterにはcountStore.定義したgetterでアクセスすることができます。

##Mutaion

CountStore.tsを書き換えましょう。

```store/CountStore.ts
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'

@Module({
  name: "CountStore",
  namespaced: true,
  stateFactory: true
})
export default class CountStore extends VuexModule {
  // 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
  private _count: number = 1

  // こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
  public get count(): number {
    return this._count
  }

  // Mutaionデコレーターを定義
  @Mutation
  increment(sumCount: number): void {
    this._count += sumCount
  }
}
```
Mutationはデコレーターで定義していきます。
Module,Mutation、これから紹介するAcrionsはデコレーターで定義なのにgettersだってgetで定義するのがちょっとわかりづらいですね笑

###受け取る

index.vueを書き換えましょう

```pages/index.vue
<template>
  <div>
    <h1>{{ countNumber }}</h1>
    <button @click="countUp">カウントアップ</button>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"


@Component
export default class Index extends Vue {
  private get countNumber(): number {
    return countStore.count
  }

  // 追加部分
  private countUp(): void {
    countStore.increment(1)
  }
}
</script>
```

Mutationはメソッドとして定義します。
これもcountStore.定義したMutationでうけとれます。


##Actionの定義

Mutationは同期的な処理しか記述できないのに対してActionは非同期処理が扱えます。
また書き換えていきましょう！

```store/CountStore.ts
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'

@Module({
  name: "CountStore",
  namespaced: true,
  stateFactory: true
})
export default class CountStore extends VuexModule {
  // 基本的にstateには直接アクセスしない方が良いのでこちらはprivateにしています
  private _count: number = 1

  // こちらがgettersの役割、stateからではなくgettersでアクセスしましょう
  public get count(): number {
    return this._count
  }

  // Mutaionデコレーターを定義
  @Mutation
  increment(sumCount: number): void {
    this._count += sumCount
  }

  // 非同期処理を定義
  @Action
  countAction() {
    setTimeout((): void => {
      this.increment(1)
    }, 3000)
  }
}

```

@Actionでデコレーターを定義することで非同期処理を書くことができます。

##受け取り
受け取る際は今まで通り

```pages/index.vue
<template>
  <div>
    <h1>{{ countNumber }}</h1>
    <button @click="countUp">カウントアップ</button>
    <button @click="countUpAction">3秒後にカウントアップ</button>
  </div>
</template>

<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator"
import { countStore } from "~/utils/store-accessor"


@Component
export default class Index extends Vue {
  private get countNumber(): number {
    return countStore.count
  }

  private countUp(): void {
    countStore.increment(1)
  }
  //受け取り
  private countUpAction(): void {
    countStore.countAction()
  }
}
</script>

```
Actionもメソッドとして定義して受け取ります。


お疲れさまでした！！