Nuxt.jsにTypeScriptを導入する手順まとめ

Nuxt.jsのプロジェクトにTypeScriptを導入するには、基本的に[公式ドキュメント](https://typescript.nuxtjs.org/ja/guide/)通りで問題ないのですが、ちょっとハマった箇所もあったので備忘録としてまとめておきます。

Nuxt.jsのバージョンは `2.11.0` 、typescriptのバージョンは `3.7.4` です。

# まずはNuxt.jsプロジェクトを作成
設定は適宜変更してください。

```bash
# プロジェクト作成
npx create-nuxt-app nuxt-ts-sample

> Generating Nuxt.js project in nuxt-ts-sample
? Project name nuxt-ts-sample
? Project description My astounding Nuxt.js project
? Author name itouuuuuuuuu
? Choose the package manager Npm
? Choose UI framework Element
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools ESLint
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
```

# typescript-buildをインストール
まずはtypescript-buildをインストールします。

```bash
npm install --save-dev @nuxt/typescript-build
```
次に `nuxt.config.js` の `buildModules` の箇所に設定を追加します。

```nuxt.config.js
export default {
  buildModules: ['@nuxt/typescript-build']
}
```

# typescript-runtimeをインストール

```bash
npm install --save-dev @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.ts の設定
`nuxt.config.js` のファイル名を `nuxt.config.ts` に変更します。
また、 `extend(config, ctx) {}` で型を指定しなければエラーになるため、指定してあげます。

```nuxt.config.ts.diff
  build: {
-    extend(config, ctx) {}
+    extend(config: any, ctx: any) {}
  }
```

`nuxt.config.ts` に下記を追加します。

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

# eslint-config-typescriptをインストール

```bash
npm install --save-dev @nuxtjs/eslint-config-typescript
```

続いて、 `package.json` の `script` の `lint` を修正します。

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

`eslintrc.js` の `extends` に下記を追加します。
`@nuxtjs` がある場合は削除します。

```eslintrc.js.diff
  extends: [
-   '@nuxtjs',
+   '@nuxtjs/eslint-config-typescript',
  ],
```

# 必要なファイルの追加
`tsconfig.json` ファイルをルート直下に作成します。

```tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "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"
  ]
}
```

# Elementの問題を解決
UIフレームワークに `Element` を使用していない場合は、この項目は飛ばしても構いません。
`Element` を選んでいる場合、 `npm run dev` を行うと下記のようなエラーが出ます。

```
92:18 Interface 'NuxtApp' incorrectly extends interface 'Vue'.
  Types of property '$loading' are incompatible.
    Type 'NuxtLoading' is not assignable to type '(options: LoadingServiceOptions) => ElLoadingComponent'.
      Type 'NuxtLoading' provides no match for the signature '(options: LoadingServiceOptions): ElLoadingComponent'.
    90 | }
    91 | 
  > 92 | export interface NuxtApp extends Vue {
       |                  ^
    93 |   $options: NuxtAppOptions
    94 |   $loading: NuxtLoading
    95 |   context: Context

ℹ Version: typescript 3.7.4
ℹ Time: 16566ms
```

これを解決するため、`tsconfig.json` の `compilerOptions` に `"skipLibCheck": true` を追加します。

```tsconfig.json
"compilerOptions": {
  "skipLibCheck": true,
}
```

# nuxt-property-decoratorをインストール
クラスを使用するために、nuxt-property-decoratorをインストールします。

```bash
npm install --save-dev nuxt-property-decorator
```
nuxt-property-decoratorを有効にするために、 `tsconfig.json` の `compilerOptions` に `"experimentalDecorators": true` を追加します。

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

# TypeScriptを使ってみる

`pages/index.vue` の `script` の箇所をTypeScriptで書いてみます。
`~/components/Logo.vue` をimportしている箇所に、`.vue` をつけることに注意してください。

```pages/index.vue
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import Logo from '~/components/Logo.vue'  // .vueを忘れずにつける
@Component({
  components: {
    Logo
  }
})
export default class extends Vue { }
</script>
```

# ESLintの問題を解決
`ESLint` を使用している場合、 `npm run dev` を行うと下記のようなエラーが出ます。

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

   8 |   },
   9 | })
> 10 | export default class extends Vue { }
     | ^
  11 | 

✖ 1 problem (1 error, 0 warnings)
```

これを解決するために、 `.eslintrc.js` の `babel-eslint` を削除します。

```.eslintrc.js
parserOptions: {
  parser: 'babel-eslint' // この行を削除
}
```

# 確認

[http://localhost:3000/](http://localhost:3000/)にアクセスして、下記の様な画面が確認できれば完了です！
お疲れ様でした！

<img width="857" alt="nuxt-ts-sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/d32a1573-884a-e5a6-347e-5abdf5b58d8a.png">