Nuxt v2.9 + Typescriptの環境構築

Nuxt2.9 + Typescript 環境構築の備忘録です。
執筆時のバージョンはNuxt2.9.2です。

2.8 => 2.9でTypescriptのインストール方法が異なるため、
調べて出てくるのが2.8以前の情報だったりで頭がバグりそうになったのでまとめます。

[公式はこちら](https://typescript.nuxtjs.org/guide/)

# はじめに
執筆中に[こちらの記事](https://qiita.com/tomopict/items/ced60c44203852faca0a)とほぼ同じことに気づきました:bow_tone1:
むしろ本記事より丁寧ですので是非ご参考ください:joy:

# 作りたい環境
- SPAモード
- ESLint
- vue-property-decoratorによるクラスベース記法

# Nuxt2.9について
Nuxt2.9以降では、以下のパッケージでTypescriptをサポートする

[Nuxt Typescript](https://typescript.nuxtjs.org/guide/)

- `@nuxt/typescript-build`  
**layouts**、**components**、**plugins**、**middlewares**でTypescriptを使用するためのNuxtモジュール
- `@nuxt/typescript-runtime`  
**nuxt.config**ファイル、**local modules**、**serverMiddlewares**のTypeScriptランタイムサポートを提供するNuxtラッパーバイナリ
- `@nuxt/types`  
型定義ファイル  
`@nuxt/typescript-build`に梱包されているので個別でのインストールは不要

要は`@nuxt/typescript-build`と`@nuxt/typescript-runtime`を入れて設定をちょこちょこっとすればいいってことね。

# 手順

## プロジェクトの作成
``` .bash
npx create-nuxt-app ts-test
# OR
yarn create nuxt-app ts-test

cd ./ts-test
```

SPAモードで、ESLintとPrettierを入れた

<details>
<summary>設定内容</summary><div>

- Project name
- Project description
- Author name
  - 任意
- Package manager
  - `Npm`
- UI framewoek
  - `None`
- Server framework
  - `None`
- Nuxt.js modules
  - `Axios`
  - `PWA`
- Linting tools
  - `ESLiint`
  - `Prettier`
- Test framework
  - `None`
- Rendering mode
  - `Single Page App`
- Development tools
  - `jsconfig.json`

</div></details>

## セットアップ
基本的に[公式](https://typescript.nuxtjs.org/guide/setup.html)を参考に進める

### @nuxt/typescript-build

``` .bash
npm install --save-dev @nuxt/typescript-build
# OR
yarn add --dev @nuxt/typescript-build
```

`nuxt.config.js`の`buildModules`に以下を記述。
今回ESLintを入れているのでこんな感じ。

``` nuxt.config.js
export default {
  // 中略
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module',
    '@nuxt/typescript-build'
  ],
  // 中略
}
```

`tsconfig.json`を[公式のとおり](https://typescript.nuxtjs.org/guide/setup.html)作成。
typesを見ると`@nuxt/types`が参照されているのが分かる。

``` tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}
```

### @nuxt/typescript-runtime

``` .bash
npm install @nuxt/typescript-runtime
# OR
yarn add @nuxt/typescript-runtime
```

`package.json`の`scripts`を、`nuxt` => `nuxt-ts`に書き換える。

```package.json
"scripts": {
  "dev": "nuxt-ts",
  "build": "nuxt-ts build",
  "generate": "nuxt-ts generate",
  "start": "nuxt-ts start"
}
```

`nuxt.config.js` => `nuxt.config.ts`に変更。
tsファイルにすると、`extend(config, ctx) {}`の部分が暗黙的anyで怒られた。

```
Parameter 'config' implicitly has an 'any' type.
Parameter 'ctx' implicitly has an 'any' type.
```

以下のように明示してあげるか、
もしくは、不要であれば`extend~~`部分をコメントアウトしても可。

```nuxt.config.ts.diff
  build: {
    /*
     ** You can extend webpack config here
     */
-    extend(config, ctx) {}
+    extend(config: any, ctx: any) {}
  }
```

つづけて、`nuxt.config.ts`に以下を追加

```nuxt.config.ts
  typescript: {
    typeCheck: true,
    ignoreNotFoundWarnings: true
  }
```

### vue-property-decorator

```.bash
npm install --save-dev vue-property-decorator
# OR
yarn add --dev vue-property-decorator
```

`tsconfig.json`に`experimentalDecorators`を追加

```tsconfig.json.diff
{
  "compilerOptions": {
+   "experimentalDecorators": true,
  }
}
```

### @nuxtjs/eslint-config-typescript
すでに`@nuxtjs/eslint-config`がインストールされていたら、これはアンインストールする
（`@nuxtjs/eslint-config-typescript`に含まれてる）

```.bash
npm uninstall @nuxtjs/eslint-config
# OR
yarn remove @nuxtjs/eslint-config
```

```.bash
npm i -D @nuxtjs/eslint-config-typescript
# OR
yarn add -D @nuxtjs/eslint-config-typescript
```

`package.json`のscriptsの、lintを修正

```package.json.diff
  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate",
-    "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
+    "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore ."
  }
```

`.eslintrc.js`を修正
`parser`を`babel-eslint` => `@typescript-eslint/parser`にする必要があるが、
これは`@nuxtjs/eslint-config-typescript`の構成に入っているので記述不要（なはず）
`extends`の`@nuxtjs`も同様の理由で削除

```.eslintrc.js.diff
  parserOptions: {
-   parser: 'babel-eslint'
  },
  extends: [
-   '@nuxtjs',
+   '@nuxtjs/eslint-config-typescript',
    'prettier',
    'prettier/vue',
    'plugin:prettier/recommended',
    'plugin:nuxt/recommended'
  ],
```

## 動作確認

Logo.vueとindex.vueのscript部分をtypescriptに書き換える

```Logo.vue
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'

@Component
export default class Logo extends Vue {}
</script>
```

```index.vue
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import Logo from '~/components/Logo.vue'

@Component({
  components: {
    Logo
  }
})
export default class IndexPage extends Vue {}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315402/a8c58980-90dd-3f2e-9563-781a348eb8d8.png)

うごいた～～:hugging:

## 一応躓いたエラーまとめ

### 躓き1

```
Module parse failed: Unexpected character '@' 以下略
```

=>
`package.json`の`scripts`を、`nuxt` => `nuxt-ts`に書き換え

### 躓き2

```
Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.
```

=>
ESLintのパーサが`babel-eslint`のままだった

## 参考
- https://typescript.nuxtjs.org/
- https://qiita.com/tomopict/items/ced60c44203852faca0a
- https://webneko.dev/posts/update-to-nuxt2-9-in-webneko-blog