【2020/02/07時点】Nuxt.js + TypeScriptでプロジェクトを始める&ESLint周りの設定について

# 概要
Nuxt.js + TypeScriptの構成でプロジェクトを始めるにあたり、最初にちょっとつまづいた点をmemoしておきます。
linting toolに、ESLint、Styelintを選択した場合が主な事例となるかなと。

ちなみに、[公式](https://typescript.nuxtjs.org/ja/guide/)のサンプルコードをもとに書いてありますので、公式を見て問題ない人はそちらでどうぞー。

この投稿で、

* Nuxt.js + TypeScript構成のプロジェクトの簡易的な立ち上げ
* 簡単な親コンポーネントと子コンポーネントを用いたプロジェクト立ち上げ

あたりは言及している感じになるかなと。

# プロジェクトを作る
## ディレクトリ構造
```
└─ sample_app
   └── .
```
`sample_app`というディレクトリにいる状態で、プロジェクトを作っていく。

## 何はともあれ、`yarn create`

```
$ yarn create nuxt-app nuxt_typescript_app
```

![スクリーンショット 2020-02-06 18.19.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/1c79270a-244a-e775-9a6f-6a53279d83d6.png)


色々と質問を聞かれるので、今回は以下のようにします。
想定として、

* Expressを使う
* Axios、DotEnvを使う
* ESLint、Prettier、StyleLintを使う
* Jestを使う
* SPAで作る

（まぁ、そこまで言及しませんが...w）

質問を選び終わると、自動的に色々やってくれて、下図のような表示がされるかと思います。

![スクリーンショット 2020-02-06 18.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/6dbfe970-16a2-3225-a297-50756b4e345f.png)

親切にも「このコマンド打ったらいいよ」と教えてくれているので、以下のコマンドを打ってみましょう。

```
$ cd nuxt_typescript_app
$ yarn dev
```

![スクリーンショット 2020-02-06 18.21.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/5e9258d2-48ce-74ec-45ee-7356f478a026.png)

はい！見事に怒られましたねw
これは、エラー文言通り、Stylelintのエラーで、

> No rules found within configuration. Have you provided a "rules" property?

「設定の中にルールが見つかんないんだけど。プロパティに"rules"って書いてある？」
みたいな感じのことを言っているので、言われた通りに修正しにいきましょう。

一旦、`Ctr`+`c`などで`yarn dev`で上がっているサーバーを止めて抜けましょう。

現時点では、`nuxt_typescript_app`ディレクトリにいるはずなので（`pwd`コマンドで確かめてもらっても良いです）、そのディレクトリ階層に`stylelint.config.js`があるはずです。

エディタで`stylelint.config.js`を開くと、

```js:stylelint.config.js
module.exports = {
  // add your custom config here
  // https://stylelint.io/user-guide/configuration
}
```
となっていると思うので、これを以下のように変えましょう。

```js:stylelint.config.js
module.exports = {
  // add your custom config here
  // https://stylelint.io/user-guide/configuration
  "rules": {}
}
```
保存したら、再度、`yarn dev`でサーバーを起動させましょう。

問題なければ、エラーが出ずに以下のような表示になるかと思います。
![スクリーンショット 2020-02-06 18.34.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/4248b27a-ec00-e9b3-09ee-dfe12343d2fe.png)

ブラウザで[http://localhost:3000](http://localhost:3000)を見てみましょう。
![スクリーンショット 2020-02-06 18.35.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/23684b5a-4596-8360-5b67-76fb12f42cbc.png)

無事、Nuxtに迎え入れられたようですね。よかった。

# TypeScriptに対応していく
## まずは`@nuxt/typescript-build`を入れる。
[公式](https://typescript.nuxtjs.org/ja/)に沿って対応していきます。

一応、先ほど作った`nuxt_typescript_app`ディレクトリ内にいることを確認しておきましょう。

```
$ pwd
~/sample_app/nuxt_typescript_app
```

確認できたら、以下のコマンドを入力します。

```
$ yarn add --dev @nuxt/typescript-build
```

色々、ぎゅわーっとインストールされて「✨  Done 」ってなったらOKです。

## 設定を変更していく
`nuxt.config.js`の中身を変更していきます。  
`nuxt.config.js`の`buildModules`という項目に`@nuxt/typescript-build`と追加記述していきます。

色々とつらつら設定が書かれていると思いますが、32行目（2020/02/06時点）あたりに、`buildModules`の設定があります。

```js:nuxt.config.js
...
/*
 ** Nuxt.js dev-modules
 */
buildModules: [
  // Doc: https://github.com/nuxt-community/eslint-module
  '@nuxtjs/eslint-module',
  // Doc: https://github.com/nuxt-community/stylelint-module
  '@nuxtjs/stylelint-module'
],
...
```

上記を以下のようにします。

```js:nuxt.config.js
...
/*
 ** Nuxt.js dev-modules
 */
buildModules: [
  // Doc: https://github.com/nuxt-community/eslint-module
  '@nuxtjs/eslint-module',
  // Doc: https://github.com/nuxt-community/stylelint-module
  '@nuxtjs/stylelint-module',
  '@nuxt/typescript-build'  // ここを追加した。
],
...
```

## `tsconfig.json`を作成する。
作成場所は、`nuxt_typescript_app`ディレクトリの階層です。  
エディタを開いて`tsconfig.json`を作成します。

```
$ emacs tsconfig.json
```
でも

```
$ vim tsconfig.json
```
でもなんでも構いません。

エディタでファイルを開いたら、[公式の設定内容](https://typescript.nuxtjs.org/ja/guide/setup.html#%E8%A8%AD%E5%AE%9A)をそのままコピペりましょう。

```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"
  ]
}
```
保存したら閉じましょう。扉もそうですが、開けたら閉める。

## `vue-shim.d.ts`を作る
今度は、`vue-shim.d.ts`という名前で同様にエディタでファイルを作り、以下を記述します。

```ts:vue-shim.d.ts
declare module "*.vue" {
  import Vue from 'vue'
  export default Vue
}
```
作成場所は`tsconfig.json`同様、`nuxt_typescript_app`ディレクトリの階層です。  
記述が終わったら、保存して閉じましょう。

ここまでで、`nuxt_typescript_app`のディレクトリは以下のような構成になっているかと思います。

```console:nuxt_typescript_appディレクトリ
.
├── README.md
├── assets
├── components
├── jest.config.js
├── layouts
├── middleware
├── node_modules
├── nuxt.config.js
├── package.json
├── pages
├── plugins
├── server
├── static
├── store
├── stylelint.config.js
├── test
├── tsconfig.json
├── vue-shim.d.ts
└── yarn.lock
```

# TypeScriptでcomponentを作ってみる。

トップページに名前を表示させるように、Userコンポーネントを作って表示させてみます。
まずは、`components`ディレクトリ配下に`UserName.vue`を作りましょう。

## `Options API`のスタイル

```vue:components/UserName.vue
<template>
  <div>
    <p>User FullName: {{ fullName }}</p>
    <p>User Message: {{ message }}</p>
  </div>
</template> 
<script lang="ts">
import Vue, { PropOptions } from 'vue'
 
interface User {
  familyName: string
  givenName: number
}
 
export default Vue.extend({
  name: 'UserName',
  props: {
    user: {
      type: Object,
      required: true
    } as PropOptions<User>
  },
  data() {
    return {
      message: 'This is a message.'
    }
  },
  computed: {
    fullName(): string {
      return `${this.user.familyName} ${this.user.givenName}`
    }
  }
})
</script> 
<style>
.user-name {
  font-size: 16px;
  color: #35495e;
}
</style>
```


## `Class API`のスタイル
[vue-property-decorator](https://github.com/kaorun343/vue-property-decorator)を使った書き方になります。
これには、少しだけ対応が必要です。

### vue-property-decoratorのインストール

```
$ yarn add vue-property-decorator
```
これでOKです。

### `<script>`ブロックのコード
`UserName.vue`の`<script>`ブロックは以下のような感じになります。
（公式を参考にしています。また、Options APIスタイルと同じ部分は省略しています。）

```vue:UserName.vue(ClassAPIスタイル) 
 <script lang="ts">
 import { Vue, Component, Prop } from 'vue-property-decorator'
 
 interface User {
   familyName: string
   givenName: number
 }
 
 @Component
 export default class UserName extends Vue {
   @Prop({ type: Object, required: true }) readonly user!: User
 
   message: string = 'This is a message.'
 
   get fullName(): string {
     return `${this.user.familyName} ${this.user.givenName}`
   }
 }
 </script>
```

### `tsconfig.json`を修正する

`vue-property-decorator`を導入して書き換えただけで、最終的にサーバーを立ち上げると、エラーが出ると思います。

> Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning. 

「デコレーターの為の実験的なサポートする機能は、将来のリリースで変更される可能性があるよ。この警告を消したかったら、`tsconfig`または`jsconfig`に`experimentalDecorators`オプションをつけてね。」

みたいな話をされます。
...ので、設定しておきましょう。

以下、設定後の`tsconfig.json`の全体像。

```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"
     ],
     "experimentalDecorators": true
   },
   "exclude": [
     "node_modules"
   ]
 }
```

# `pages/index.vue`を編集する

子コンポーネント側に値を渡すように`pages/index.vue`を修正していきます。

```vue:pages/index.vue
<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt_typescript_app
      </h1>
      <h2 class="subtitle">
        My epic Nuxt.js project
      </h2>
      <user-name :user="userName" />
      <div class="links">
        <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>
      </div>
    </div>
  </div>
</template>

<script>
import Logo from '~/components/Logo.vue'
import UserName from '~/components/UserName.vue'

export default {
  components: {
    Logo,
    UserName
  },
  data() {
    return {
      userName: {
        familyName: '田中',
        givenName: '1234'
      }
    }
  }
}
</script>
```

* `UserName.vue`コンポーネントをimport。
* `compoents`プロパティに`UserName`を記載。
* `<tamplate>`ブロック部分に`<user-name />`という形でimportしてきたコンポーネントを入れ込む。
* `<tamplate>`ブロック部分に入れ込んだ`<user-name />`に子コンポーネントに渡す値を記述する(`:user-name="userName"`)。
* `<script>`ブロックの`data`プロパティに`userName`という名前のオブジェクトを記述し、データを返すようにする。
  * 子コンポーネントの`interface User {...}`で、`familyName`のデータ型は`string`。`givenName`のデータ型は`number`にしてあるので、`familyName`には文字列を、`givenName`には数字を記述するようにします。
  * 子コンポーネントで`props.user`自体はのデータ型は、`Object`になっている。


# いざ、起動...！！
...で、多分エラーを吐きますw

![eslint-fail 2020-02-07 12.02.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/ffb67722-39fd-87fb-fbb2-c4a8b6c98725.png)

まるで`interface`部分の記述が間違っているようなエラーですが、そうではなく、parser周りの設定のエラーになります。

## ということで直していく

...というか、公式を読んでいれば、実は[Lintという項目](https://typescript.nuxtjs.org/ja/guide/lint.html#%E8%A8%AD%E5%AE%9A)があるので、そこでちゃんと言及しています（ちゃんと読め！という話ですねw）。

> 必要なことは、@nuxtjs/eslint-config-typescript をインストールすることだけです：

と、yarnで`@nuxtjs/eslint-config-typescript`をインストールするだけと書いてありますので、やっていきます。

```
$ yarn install -D @nuxtjs/eslint-config-typescript
```
でインストール。

インストールするだけというのですが、実際は`.eslintrc.js`を弄ります（基本あると思いますが、なければ作成を）。

ここまで作成していると以下のような状態になっているはずです。

```js:.eslintrc.js
 module.exports = {
   root: true,
   env: {
     browser: true,
     node: true
   },
   parserOptions: {
     parser: 'babel-eslint'
   },
   extends: [
     '@nuxtjs',
     'prettier',
     'prettier/vue',
     'plugin:prettier/recommended',
     'plugin:nuxt/recommended'
   ],
   plugins: [
     'prettier'
   ],
   // add your custom rules here
   rules: {
     'nuxt/no-cjs-in-config': 'off'
   }
 }
```
このファイルの`extends`の部分に`@nuxtjs/eslint-config-typescript`を加えます。
実際は、`@nuxtjs`を`@nuxtjs/eslint-config-typescript`にすればOKです。


```js:.eslintrc.js
 module.exports = {
   root: true,
   env: {
     browser: true,
     node: true
   },
   parserOptions: {
     parser: 'babel-eslint'
   },
   extends: [
     '@nuxtjs/eslint-config-typescript', //ここを変更。
     'prettier',
     'prettier/vue',
     'plugin:prettier/recommended',
     'plugin:nuxt/recommended'
   ],
   plugins: [
     'prettier'
   ],
   // add your custom rules here
   rules: {
     'nuxt/no-cjs-in-config': 'off'
   }
 }
```

ただ、このままだとまだエラー解消はされません。
公式には、`WARNING`でなんかパーサーのことを書いてあります。
...が、変な日本語訳？なおかげで、自分は最初いまいち意図を掴みきれなかったのですが、ざっくりと「パーサーの設定気をつけてね！」と捉えましたw

## パーサーの設定も変える
現在、`babel-eslint`になっていると思います。
これを`@typescript-eslint/parser`に変えていきます。

```js:.eslintrc.js
 module.exports = {
   root: true,
   env: {
     browser: true,
     node: true
   },
   parserOptions: {
     parser: '@typescript-eslint/parser' // babel-eslintから変更。
   },
   extends: [
     '@nuxtjs/eslint-config-typescript', 
     'prettier',
     'prettier/vue',
     'plugin:prettier/recommended',
     'plugin:nuxt/recommended'
   ],
   plugins: [
     'prettier'
   ],
   // add your custom rules here
   rules: {
     'nuxt/no-cjs-in-config': 'off'
   }
 }
```

修正したら、保存しましょう。
そして、すでにyarnでサーバーを上げていたら一旦`Ctr`+`c`とかで抜けてから、再度`yarn dev`でサーバーを立ち上げてみましょう。

# 上手くいくと...
![スクリーンショット 2020-02-07 12.58.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232071/fa8652c7-9327-c0d4-028b-b7427f6dec76.png)

こんな感じで、タイトルの下にユーザー名とメッセージのコンポーネント部分が反映されて表示されると思います。

# 個人的なまとめと要点
ポイントは、ESLint周りのparser設定だと思います。
ちゃんと読んでいなかった自分もいけないのですが、上記設定のことがサラっと流されていて、ちょっと困りました。。。

また、「nuxt typescript Parsing error: Unexpected token」みたいなキーワードでググっても、なかなか似たようなことは出てこず、若干ハマったので、メモとして残しておきます。