【Nuxt.js】Todoリストで理解するTypeScriptでVuex入門

簡単にTypeScriptでVuex入門をしたかったのでTodoリストを作ってみました。
（※pageの記載はtsですが、ストアはjsで実装しています）

# この記事で伝えること
- pages/index.vueでのストアの呼び出し方
- JavaScriptとTypeScriptの比較

# この記事で伝えないこと
- 環境構築
- TypeScriptでのストアの書き方
- Vuexの解説

# ①ストアを作成（JavaScript）

下記リンクを参考にストアを作っていきます
* [Vuex ストア - Nuxt.js](https://ja.nuxtjs.org/guide/vuex-store/#%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%83%A2%E3%83%BC%E3%83%89)

`ディレクトリ構成`

- pages
    - index.vue（TypeScriptでのtodoリスト）
    - todos.vue（JavaScriptでのtodoリスト）
- store（ストアはJS・TS共に共通）
    - index.js
    - todos.js

```store/index.js
export const mutations = {
  add(state, text) {
    state.todos.list.push({
      text: text,
      done: false
    })
  },
  remove(state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle(state, todo) {
    todo.done = !todo.done
  }
}
```

```store/todos.js
export const state = () => ({
  list: []
})
```

モジュールモードなので、明示的に `new Vuex.Store` する必要はありません。

>モジュールモード: store ディレクトリ内のすべての *.js ファイルが 名前空間付きモジュール に変換されます（index はルートモジュールとして存在します）

# ②まずはお馴染みのJavaScriptで実装

```pages/todos.vue
<template>
  <ul>
    <li v-for="(todo, i) in todos" :key="i">
      <input type="checkbox" :checked="todo.done" @change="toggle(todo)" />
      <span :class="{ done: todo.done }">{{ todo.text }}</span>
    </li>
    <li>
      <input placeholder="What needs to be done?" @keyup.enter="addTodo" />
    </li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  computed: {
    todos() {
      return this.$store.state.todos.list
    }
  },
  methods: {
    addTodo(e) {
      this.$store.commit('add', e.target.value)
      e.target.value = ''
    },
    ...mapMutations({
      toggle: 'toggle'
    })
  }
}
</script>

<style>
.done {
  text-decoration: line-through;
}
</style>
```

このコードでしっかりと動作・エラーが出ないことを確認します。

# ③TypeScriptで実装
上記のコードをTypeScriptで書くとこのようになります。

```pages/index.vue
-- template内は上記コードと同様 --

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import { mapMutations } from 'vuex'

@Component({
  methods: {
    ...mapMutations({ toggle: 'toggle' })
  }
})
export default class extends Vue {
  private get todos(): Object {
    return this.$store.state.todos.list
  }

  addTodo(e): void {
    this.$store.commit('add', e.target.value)
    e.target.value = ''
  }
}
</script>
```

#### TypeScriptでの変更点は以下です。

##### vue-property-decoratorの導入
* [kaorun343/vue-property-decorator: Vue.js and Property Decorator](https://github.com/kaorun343/vue-property-decorator)

##### methods
メソッドはクラスのメソッドになるため、 `methods: {}` の記述は不要です。

##### computed
computedは *getter / setter* で記述します。

##### map系ヘルパー（Vuex）
メソッドは上記のようにクラスのメソッドとして記述しますが、
*...mapMutations* を使う場合、@Component内にそのまま書きます

ちなみに、下記記事によるとmap系ヘルパーを使うと全てanyになってしまうため、TypeScriptで記述する恩恵が少ないみたいです。
→ [Vuexによる状態管理を含む最高に快適な Vue.js + TypeScript の開発環境を目指す話 - Qiita](https://qiita.com/potato4d/items/c9c0c8e674f20c85948a)

以上で、`<script>`から`<script lang="ts">`化ができました！
Vue（Nuxt）でTypeScriptを書く際は、すこしクセがあるのでJSとの違いがわかるVuexのコードを見たくて書きました。
ストアをTS化したらまた書きたいと思います。

# 参考資料

めちゃくちゃわかりやすいです
上記以外のTypeScriptの書き方が不明な方はこちらを参考に
* [TypeScriptでVue.jsを書く – Vue CLIを使った開発のポイントを紹介 | maesblog](https://mae.chab.in/archives/60167)

ストアをTS化するときに参考にしたいと思ってます
* [VuexをTypeScriptで利用するのに悩んだ | cloudpack.media](https://cloudpack.media/42966)

クラスの書き方がわからないときはこちら
* [vuejs/vue-class-component: ES / TypeScript decorator for class-style Vue components.](https://github.com/vuejs/vue-class-component)

Vuexのmap系ヘルパーをどう使えば良いか悩みました
こちらを参考に@Component内に記述しました
* [How to use `...mapGetters` with class components? Typescript errors · Issue #109 · vuejs/vue-class-component](https://github.com/vuejs/vue-class-component/issues/109)