Nuxt.js v2.6でTypeScriptが書ける環境を構築する

先日、[Nuxt.js v2.6がリリース](https://qiita.com/iwata@github/items/f798cc6cc24ac41a74d9)されましたね！

本バージョンから`nuxt-ts`コマンドを使わずに、TypeScriptで記述できるようになったという特徴がありますが、`create-nuxt-app`を叩いてからTypeScriptの環境を整えるために案外手間取ったので、記事にしておきます。

# 手っ取り早く始めたい人

<a href="https://github.com/dora1998/nuxt-ts-scaffold"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/dora1998/nuxt-ts-scaffold.png" width="460px"></a>

こちらに以下で行う作業を済ましたものを上げてありますので、cloneしてご自由にお使いください。
若干`typesync`や`vue-property-decorator`など、別記事で説明する(予定の)ライブラリも入っています。

# まずは create-nuxt-app

とりあえずここでは、SPAとしてプロジェクト作成を進めていきます。また、LinterとPrettierは便利なので是非つけておきましょう。その他はお好みで。

※`typescript-test`としているところは、プロジェクト名なので適宜変更してください。

```bash
$ create-nuxt-app typescript-test
> Generating Nuxt.js project in /Users/hoge/Projects/typescript-test
? Project name typescript-test
? Project description My beautiful Nuxt.js project
? Use a custom server framework none
? Choose features to install Linter / Formatter, Prettier, Axios
? Use a custom UI framework none
? Use a custom test framework none
? Choose rendering mode Single Page App
? Author name dora1998
? Choose a package manager yarn
```

一旦ここで起動することを確認しておきましょう。

```bash
$ cd (project-name)
$ yarn run dev
```

# TypeScript 仕様に変更する

## Nuxt.js のバージョンを変える

デフォルトで入るパッケージのバージョンは古い(v2.4系)なので、まず最新のv2.6までバージョンアップします。
自分で`package.json`を書き換えてもいいのですが、`ncu`を使うのが楽です。`ncu`を実行して、最新バージョンを確認してみましょう。

```bash
$ ncu
Checking /Users/hoge/Projects/typescript-test/package.json
[====================] 22/22 100%

 ...(前略)
 nuxt                       ^2.4.0  →    ^2.6.1 
 ...(後略)

Run ncu -u to upgrade package.json
```

nuxtがv2.6以上になることを確認したら、これを`package.json`に反映しましょう。

```bash
$ ncu -u
```

`node_modules`フォルダと`yarn.lock`(npmの場合は`package-lock.json`)を削除し、一旦今入れているパッケージを入れ直します。

```bash
$ rm -rf node_modules yarn.lock
$ yarn
```

## パッケージの導入

まずは、TypeScript本体とNuxt.jsとの連携に必要なものから入れていきましょう。

```bash
$ yarn add typescript ts-node @nuxt/typescript
```

## 設定ファイルの作成・修正

### tsconfig.json

次に、`tsconfig.json`をプロジェクト直下に作成します。

```json:tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "types": [
      "@types/node",
      "@nuxt/vue-app"
    ],
    "paths": { "@/*": [ "./*" ], "~/*": [ "./*" ] },
    "target": "es5",
    "strict": true,
    "module": "es2015",
    "moduleResolution": "node"
  },
}
```

この中にある`paths`の設定がないと、

```typescript
import Logo from '~/components/Logo.vue'
```

といったimport文が解決できないので注意しましょう。

### nuxt.config.js → nuxt.config.ts

設定ファイルもTypeScriptで書くように変更します。拡張子を`js`→`ts`に修正する他、下の部分を修正すると設定オブジェクト自体にも型がつきます。

```javascript:nuxt.config.js
module.exports = {
 ...
}
```

```typescript:nuxt.config.ts
import NuxtConfiguration from '@nuxt/config'

const nuxtConfig: NuxtConfiguration = {
 ...
}
export default nuxtConfig
```

また一点、undefined の可能性があるためにエラーを吐く箇所があるので、そちらも修正します。

```typescript:nuxt.config.ts
  build: {
    extend(config, ctx) {
      if (ctx.isDev && ctx.isClient) {
        if (!config.module) return  // undefinedの場合、pushせずにreturnするように追加
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  }
```

## コンポーネントをTypeScriptで書く

それでは、まず最初からある`index.vue`を TypeScript に変更してみましょう。まずは、下のように変更してみます。

```vue:index.vue
<script lang="ts">
import Vue from 'vue'
import Logo from '~/components/Logo.vue'

export default Vue.extend({
  components: {
    Logo
  }
})
</script>
```

なお、この書き方ではほぼjsと変わらないので、TypeScript の良さを活かせていると言えません。別記事で説明する(予定の)`vue-property-decorator`を使うと、よりスマートに書くことができます。

ここで、このままだとビルドは通るのですが、VSCode の拡張機能である Vetur がエラーを吐いてしまいます。

<img width="607" alt="nuxt_ts_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/51163/1365f9e0-3b9e-98ac-1b7e-c35fc13005ed.png">

.vueファイルをうまく認識してないのかなと定義ファイルを作ってみたりしたのですが、実はこれは`Logo.vue`に`<script></script>`がないことが原因です。ですので、

```vue:Logo.vue
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({})
</script>
```

という風に追記してあげれば解決します。これで、エラーなく動くはずです。

## ESLint で TypeScript を扱う設定

次に、TypeScript についても Lint が効くように設定します。TSLint を使う場合も多いかと思いますが、ここでは[最近力を入れ始めた](<https://qiita.com/mysticatea/items/aaf677928e965abe093d>) ESLint の方で TypeScript の Lint を行う方で設定したいと思います。

まずは、必要なパッケージをインストールしましょう。

```bash
$ yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin 
```

その後、`.eslintrc.js`を次のように修正します。

```javascript:.eslintrc.js
module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    project: './tsconfig.json',
    ecmaFeatures: { "legacyDecorators": true }
  },
  extends: [
    '@nuxtjs',
    'plugin:nuxt/recommended',
    'plugin:prettier/recommended',
    'prettier/vue',
    'prettier/@typescript-eslint'
  ],
  plugins: [
    'prettier',
    '@typescript-eslint'
  ],
  // add your custom rules here
  rules: {
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "error"
  }
}
```



ここで、一番下の`rules`で`no-unused-vars`を`@typescript-eslint`のルールで上書きしていますが、これをしないと`nuxt.config.ts`で

```
'NuxtConfiguration' is defined but never used.eslint(no-unused-vars)
```

というようなエラーが発生します。このように、ESLint 本家が持つルールの一部を TypeScript ファイルにそのまま適用してしまうと、不適切なエラーを吐くことがあります。このことについては、以下の記事が参考になります。

[@typescript-eslint ことはじめ - teppeis blog](<https://teppeis.hatenablog.com/entry/2019/02/typescript-eslint>)

これでひとまず**環境構築は終了**です！

## トラブルシューティング

### index.vue で Lintエラー

ESLint(正確にはPrettier) が`index.vue`について、

```
/Users/hoge/Projects/typescript-test/pages/index.vue
  12:11  error  Replace `⏎··········href="https://nuxtjs.org/"⏎··········target="_blank"⏎·········` with `·href="https://nuxtjs.org/"·target="_blank"`  prettier/prettier
  16:9   error  Replace `>Documentation</a` with `··>Documentation</a⏎········`                                                                         prettier/prettier
  21:9   error  Replace `>GitHub</a` with `··>GitHub</a⏎········`                                                                                       prettier/prettier
```

といったようなエラーを吐くことがあります。整形スタイルが正しくないだけなら、普通は自動修正できるのですが、なぜかうまくいかないときがあります。
その場合は、ESLint と Prettier の設定を特にいじっていなければ、

```html:index.vue
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >Documentation</a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >GitHub</a>
```

と書いてある部分を

```html:index.vue
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
```

と修正してあげれば、解消されるはずです。

# 環境は整えた…その次は？

これで、とりあえず Nuxt.js を TypeScript で書く最低限の環境が整いました！

しかし、せっかく TypeScript を導入したのですから、JS で書いていた時よりも簡潔に、また型安全に安全に書けたら嬉しいですよね。本記事もだいぶ長くなってきたので、開発を楽にする`*vue-property-decorator*`や`vuex-modules-decorator`の導入と使い方について書いていきたいと思います。

長文お読みいただきありがとうございました！もしなにか詰まった点やここが違うぞとかあれば、お気軽にコメントや編集リクエストしてください。