今一度、NuxtJS v2.11.0でTypeScript v3.7.5を適用する方法を探してみた。

# 環境
- Vue.js v2.6.11
- NuxtJS v2.11.0
- TypeScript v3.7.5

ご親切なお方がいらっしゃいましたら、間違っている箇所に関しては変更依頼を出していただけると幸いです。自身でも気づいた場合には速やかに修正を行います。

# NuxtJSでTypeScriptを設定する
https://typescript.nuxtjs.org/guide/setup.html#installation

ドキュメントに従い、packageのインストールと、ビルド時の設定を行う。

```sh
npm i -D @nuxt/typescript-build
touch tsconfig.json vue-shim.d.ts
```

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

```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
declare module "*.vue" {
  import Vue from 'vue'
  export default Vue
}
```

# 従来のAPIの場合
https://typescript.nuxtjs.org/ja/cookbook/components/#template Options API

最も既存のコードから変更が少ない方法をまず試してみた。`Vue.extend`を使うだけのやり方である。

```ts
export interface Animal {
  name: string
  link: string
}
export default Vue.extend({
  name: 'AnimalComponent',
  props: {
    title: {
      type: String,
      required: true
    },
    content: {
      type: String,
      required: true
    },
    animals: {
      type: Object,
      required: false
    } as PropOptions<Array<Animal>>
  }
})
```

#### `npm run dev` によるエラーは発生しないが、ブラウザ上で実行時に下記のプロパティエラーが発生する
ビルド時に型解決したものが実行時にエラーになるというのがなぜかよくわからない。
何かいい方法が見つかればよかったのだが、ドキュメントにこれ以上の情報がないので、一旦別の方法を探すことにした。

```js
vue.runtime.esm.js?2b0e:619 [Vue warn]: Invalid prop: type check failed for prop "animals". Expected Object, got Array 
```

# クラススタイルのVueコンポーネントの場合
https://typescript.nuxtjs.org/cookbook/components/#template Class API

割と主流の書き方だと思われる。また、TypeScriptとして扱うためのデコレータを使うので、理解しやすい。ただし、その反面次のコンポジションAPIとの思想の差がある。

```sh
npm i -S vue-property-decorator
```

```ts
import { Vue, Component, Prop } from 'vue-property-decorator'
export interface Animal {
  name: string
  link: string
}
export default class AnimalComponent extends Vue {
  @prop()
  title: string
  @prop()
  content: string
  @prop()
  animals: Array<Animal>
}
```

ビルド時にエラーが出ず、実行時にエラーもブラウザ上で出なかったが、実行すると今度はterminal側のconsoleに以下のエラーが出た。

```js
10:22 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.json
{
  "experimentalDecorators": true
}
```

次は、以下のエラーが発生している。これは、プロパティの初期化時のチェックを厳格にしているがゆえに発生するエラー。

```js
Property 'title' has no initializer and is not definitely assigned in the constructor.
Property 'content' has no initializer and is not definitely assigned in the constructor.
Property 'animals' has no initializer and is not definitely assigned in the constructor.
```

今回は、初期化時のチェックの必要がないので、以下の対応を行う

```ts
import { Vue, Component, Prop } from 'vue-property-decorator'
export interface Animal {
  name: string
  link: string
}
export default class AnimalComponent extends Vue {
  @prop()
  title!: string
  @prop()
  content!: string
  @prop()
  animals!: Array<Animal>
}
```

# コンポジションAPIの場合
https://www.vuemastery.com/courses/vue-3-essentials/why-the-composition-api/
とりあえずなにこれ？と思い、なぜコンポジションAPIか？という動画だけ見れたのでスキップ気味にサクッと見た。

## 既存Vue.jsの課題点
Vue.jsのコンポーネントを作成して、機能追加や変更を加え続けると、そのコンポーネントが肥大化してくるので、内容を把握しにくくなってくること

## 課題への解決策
1. なので、そのコンポーネントの中を整頓して意味を付けたブロックごとに分割してMixinする
  - Mixinパターンの解決策の欠点は名前空間の強いルール付が必要なことや、中身を読まないといわゆるプロパティ情報が不透明であること
2. slotを利用する
  - 個人的にもMixinに比べコンポーネント単位で分割されていたりして、Vue.jsっぽいなぁと思い好んでいるが、slotで分割する分だけコンポーネントが増加するのでパフォーマンスが悪いと言われる

https://vue-composition-api-rfc.netlify.com/#summary
RFCも読んでみると、既存の課題解決方法の欠点を補うためにコンポジションAPIを使ったやり方に変えたいみたいだ。

ということで、3系を念頭にこの戦略を取るのは一つの手ではある。
ただし、今回は2系で主流のやり方をまとめたかったのでここは割愛した。

# 最後に
下記のように、Vue.jsが3系になる事を考えながら書くというのも一つの手だと思われる
https://stackoverflow.com/questions/58367558/best-practices-for-easy-migration-from-vuejs2-to-vuejs3