[Vue+TypeScript] Vue.extend で Vue らしさを保ちつつ TypeScript で書くときの型宣言についてまとめた

# はじめに

Vue + TypeScript の組み合わせでVueを書くときに、`vue-property-decorator` を利用して書いていくことが多いと思います。
ただ `vue-property-decorator` を利用すると、どうしてもVueらしさがなくなるというか、よりTypeScriptにらしい書き方になると感じています。
せっかくJavaScriptでVue書けるようになったのに、全然書き方が違うじゃないか…と挫折しかけることもあるんじゃないでしょうか？
ちなみに私は `vue-property-decorator` で書くほうが慣れているので好きですが、Vue入門者には厳しいところがあると思うので、 `Vue.extend` ベースでTypeScriptを書いていくという方法を紹介するのと、その際の型宣言についてもまとめていこうと思います。

VueをTypeScriptで書きたいけど、 `vue-property-decorator` は使いたくない…って人の参考になればいいなーと思います。

## Vue.extend ??

TypeScript内でVueモジュールをimport/extendして書く方法です。
JavaScriptでのVueコンポーネントの記述に近い書き方でTypeScriptを書くことができます。

```vue
<script lang="ts">
  import Vue from "vue"
  
  export default Vue.extend({
    name: "component",
    data() {
      return {
        value: "hoge"
      }
    }
  })
</script>
```

## 環境構築

VueCLIで簡単に構築することができます。
TypeScriptを選択して、class-styleコンポーネントシンタックスを利用しないと選択すればいいです。

```terminal
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
 ◯ Babel
❯◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◯ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing
```

```terminal
? Use class-style component syntax? (Y/n) n
```

これで `Vue.extend` の環境が構築されます。簡単ですね。

## Vue.extend における型

Vue.extend ベースでTypeScriptで書いていく際の型宣言を紹介していこうと思います。

### props

Propについてはネイティブコンストラクターを付与することで、内部で型推論されます。
またArrayやObjectの詳細な型宣言については `PropType` を利用することで宣言することができます。

```vue
<script lang="ts">
  import Vue, { PropType } from "vue"

  export type PropObjType = {
    id: string
    index: number
  }

  export default Vue.extend({
    props: {
      val: {
        type: String,
        default: ""
      },
      obj: {
        type: Object as PropType<PropObjType>,
        default: () => ({
          id: "",
          index: 0
        })
      }
    }
  })
</script>
```

ここで注意が必要なのが、ビルド時のコンパイルエラーを得ることができないという点です。ただ実行時のエラーを得ることはできます。
また用意されているネイティブコンストラクターは以下の通りです。

- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol

### data

data()関数に向けて型を定義し、型アノテーションを付与します。

```vue
<script lang="ts">
  import Vue from "vue"
  
  export type DataType = {
    value: string
    enable: boolean
    count: number
  }

  export default Vue.extend({
    data(): DataType {
      return {
        value: "hoge",
        enable: true,
        count: 0
      }
    }
  })
</script>
```

### lifecycle hooks

lifecycle hooksで着火するイベントは基本的に `void` 型の関数

```vue
<script lang="ts">
  import Vue from "vue"
  
  export default Vue.extend({
    created(): void {
      console.log("Created!!!")
    }
  })
</script>
```

### computed

computedで呼び出される関数が返す値に対する型を宣言する

```vue
<script lang="ts">
  import Vue from "vue"
  
  export type DataType = {
    value: string
    enable: boolean
    count: number
  }
  
  export default Vue.extend({
    data(): DataType {
      return {
        value: "hoge",
        enable: true,
        count: 0
      }
    },

    computed: {
      isEnabled(): boolean {
        return this.enable
      },
      getCount(): number {
        return this.count
      }
    },
  })
</script>
```

### methods

methodsで呼び出される関数が返す値に対する型を宣言する

```vue
<script lang="ts">
  import Vue from "vue"
  
  export type DataType = {
    value: string
    enable: boolean
    count: number
  }
  
  export default Vue.extend({
    data(): DataType {
      return {
        value: "hoge",
        enable: true,
        count: 0
      }
    },

    methods: {
      countUp(): void {
        this.count += 1
      },
      getValue(): string {
        return this.value
      }
    }
  })
</script>
```

## まとめ

- Vueらしさを保ちつつTypeScriptで書きたいって人 → Vue.extend
- VueらしさよりTypeScriptらしく書きたいって人 → vue-property-decorator

という感じかなって思います。自分に合った方法でより楽しくVueを書いていきましょう！
ではまた！！！