Vue.jsでTypeScriptを使う(with vue-property-decorator)

# Vue.jsのscript内に型定義有りのコードを書きたい

通常のJSで書いていくのは辛いので、[vue-property-decorator](https://github.com/kaorun343/vue-property-decorator)を利用してTypeScriptで開発していきます。
対象のプロジェクトにはあらかじめTypeScriptがインストールされているものとします。[^1]

[^1]: 前回の記事[vue-cli3 + TypeScript + Electronでアプリを開発する](https://qiita.com/paragaki/items/e7d3a43b50233af96424)を参照

# インストール

```console
$ npm install --save vue-property-decorator
```

# 基本
## Component

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

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

これはJavaScriptで以下のように書いたものと同じです。

```javascript
<script>
export default {
  name: 'MyComponent'
}
</script>
```

Vue.jsとTypeScriptでコンポーネントのロジック部分を書いていく際は`vue-property-decorator`というパッケージを利用します。
`name`は明示的に宣言することもできますが、クラス名として宣言することで省略可能です。

## Data

コンポーネント内のdataは、クラスのメンバ変数として宣言します。
比較のために、JavaScriptで書くケースも載せておきます。

```javascript
// TypeScript
@Component
export default class MyComponent extends Vue {
  private msg = 'サンプルメッセージ'
  private users: Array<string> = [
    '山田',
    '田中'
  ]
}

// JavaScript
export default {
  data() {
    return {
      msg = 'サンプルメッセージ',
      users: [
        '山田',
        '田中'
      ]
    }
  }
}
```

## Computed

TypeScriptではcomputedはプレフィックス`get`をつけて記述します。
戻り値として指定した型と`return`で返す値の型は一致させる必要があります。

```javascript
// TypeScript
export default class MyComponent extends Vue {
  get profile(): string {
    return `Hi, My name is ${this.name} and I am ${this.age} years old!`
  }
}

// JavaScript
export default {
  computed: {
    profile() {
      return `Hi, My name is ${this.name} and I am ${this.age} years old!`
    }
  }
}
```

## Props

`vue-property-decorator`から`{ Prop }`をインポートし、コンポーネント内に記述します。
JavaScriptのPropsの受け渡しと同様に、TypeやRequired等の属性を指定できます。

```javascript
// TypeScript
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
  @Prop() message: string
  @Prop({ default: '山田' }) name: string
  @Prop({ required: true }) age: number
  @Prop({ type: String, required: false, default: 'Nagoya' }) address: string 
}

// JavaScript
export default {
  props: {
    message,
    name: {
      default: '山田'
    },
    age: {
      required: true
    },
    address: {
      type: String,
      required: false,
      default: 'Nagoya'
    }
  }
}
```

## Methods

```javascript
// TypeScript
import { Component, Vue } from 'vue-property-decorator'
@Component
@Component
export default class MyComponent extends Vue {
  public incrementAge(): void {
    this.age++
  }
  public sum(num1: number, num2: number): number {
    return num1 + num2
  }
}

// JavaScript
export default {
  methods: {
    incrementAge() {
      this.age++
    },
    sum(num1, num2) {
      return num1 + num2
    }
  }
}
```

## Watch

`@Watch`を宣言して以下のように書きます。

```javascript
// TypeScript
import { Vue, Component, Watch } from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
  private message: string
  private age: number

  @Watch('message')
  changedMessage() {
    this.message = 'The message has been changed.'
  }
  @Watch('age')
  changedAge(newVal: number, oldVal: number) {
    console.log(`new: ${newVal}, old: ${oldVal}`)
  }
}

// JavaScript
watch: {
  message() {
    this.message = 'The message has been changed.'
  },
  age(newVal, oldVal) {
    console.log(`new: ${newVal}, old: ${oldVal}`)
  }
}
```

# おわりに

今回はVue.jsとTypeScriptでdecoratorを使用したComponent, Data, Props, Methods, Watchの書き方を説明しました。
他にも方法があるようなので、また勉強して記事を書こうと思います。
最後まで読んでいただき、ありがとうございました！