Help us understand the problem. What is going on with this article?

Vue.js to TypeScriptの書き方一覧

最近ひたすら既存のVueプロジェクトのTypeScript化をやっているのでメモとしてまとめます。

(2019/09/09 追記)
vue-class-componentはvue3のRFCから外されたようで、現在は、Composition APIがRFCとなっています。
Composition APIでのTypeScript化についてはこちらの記事をどうぞ
https://qiita.com/ryo2132/items/f055679e9974dbc3f977

前提

  • .vueファイルで<script lang="ts">を使う
  • vue-property-decoratorを使ったデコレータ方式のTypeScript化を行う

data

dataはそのままクラスのプロパティとして宣言します。

JavaScript

<script >
export default {
  name: 'Human',
  data() {
    return {
      name: '山田 太郎',
      age: 19
    };
  }
};
</script>

TypeScript

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

@Component
export default class Human extends Vue {
  name: string = '山田 太郎';
  age: number = 19;
}
</script>

components

SFCで他Vueコンポーネントを参照する際に使うComponentsは
@Component()デコレータにcomponentsをkeyに持つオブジェクトを渡し定義します.

JavaScript

<script>
import Header from './Header'
import Footer from './Footer'

export default {
  name: 'Page',
  components: {
    Header,
    Footer
  }
}
</script>

TypeScript

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Header from './Header.vue';
import Footer from './Footer.vue';

@Component({
  components: {
    Header,
    Footer
  }
})
export default class Page extends Vue {
}
</script>

directive

v-hogeのような独自のdirectiveを追加する場合はそのまま@components()にオブジェクトを渡す

JavaScript

<script>
import HogeDirective from './HogeDirective'

export default {
  name: 'Page',
  directives: {
    hoge: HogeDirective
  }
}
</script>

TypeScript

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HogeDirective from './HogeDirective'

@Component({
  directives: {
    hoge: HogeDirective
  }
})
export default class Page extends Vue {
}
</script>

props

親コンポーネントからデータを受け取るpropsは@Propデコレータを使い定義します。

@Prop(options: (PropOptions | Constructor[] | Constructor) = {})

JavaScript

<script>
export default {
  name: 'Post',
  props: {
    contents: { default: '', type: String, require: true },
    postNumber: { default: 0, type: [Number, String], require: true },
    publish: { default: true, type: Boolean, require: true },
    option: { type: Object, require: false } // optionには {new: boolean, important: boolean, sortNumber: number} が設定される想定
  }
}
</script>

TypeScript

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

@Component
export default class Post extends Vue {
  @Prop({ default: '' })
  contents!: string;

  @Prop({ default: 0 })
  postNumber!: number | string;

  @Prop({ default: false })
  publish!: boolean;

  @Prop
  option?: {
    new: boolean,
    important: boolean,
    sortNumber: number
  };
}
</script>

computed

算出プロパティはgetter付きのメソッドで定義します。

JavaScript

<script>
export default {
  name: 'Human',
  data () {
    return {
      firstName: '太郎',
      lastName: '山田'
    }
  },
  computed: {
    fullName () {
      return `${this.firstName} ${this.lastName}`
    }
  }
}
</script>

TypeScript

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

@Component
export default class Human extends Vue {
  firstName: string = '太郎';
  lastName: string = '山田';

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

}
</script>

emit

親コンポーネントに値を送信する$emitは@Emitデコレータを使いfunctionとして定義します。
メソッド名が、$emitに渡すイベント名がメソッド名と同様の場合は、@Emit()の引数(イベント名)は省略できます。
※ 以下のcountUpのケース。camelCaseとkebabe-caseは自動で変換されます

@Prop(options: (PropOptions | Constructor[] | Constructor) = {})

JavaScript

<script>
export default {
  name: 'Counter',
  data () {
    return {
      count: 0
    }
  },
  methods: {
    countUp (n) {
      this.count += n
      this.$emit('count-up', n)
    },
    resetCount () {
      this.count = 0
      this.$emit('reset')
    }
  }
}
</script>

TypeScript

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

@Component
export default class Counter extends Vue {
  count: number = 0;

  @Emit('count-up')
  countUp(n) {
    this.count += n;
    return n;
  }

  @Emit('reset')
  resetCount() {
    this.count = 0;
  }
}
</script>

model

input要素のコンポーネント化の際にvalueが競合しないように用いるmodelは
@Modelデコレータを使ってプロパティを定義します。

@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})

JavaScript

<script>
export default {
  name: 'MyRadio',
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: { type: Boolean }
  }
}
</script>

TypeScript

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

@Component
export default class MyCheckBox extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean;
}
</script>

watch

dataの変更を検知するWatchは@Watchデコレータを使ってメソッドを定義します。
optionにwatchオプションのimmediateやdeepもオブジェクトとして渡すことで指定できます。

@Watch(path: string, options: WatchOptions = {})

JavaScript

<script>
export default {
  name: 'InputText',
  data () {
    return {
      text: '',
      lengthDiff: 0
    }
  },
  watch: {
    text: function (newText, oldText) {
      this.lengthDiff = newText.length - oldText.length
    }
  }
}
</script>

TypeScript

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

@Component
export default class InputText extends Vue {
  text: string = '';
  lengthDiff: number = 0;

  @Watch('text')
  onTextChanged(newText: string, oldText: string) {
    this.lengthDiff = newText.length - oldText.length;
  }
}
</script>

provide, inject

深い階層でも親コンポーネントと子コンポーネントでデータを共有する際に用いるprovideとinjectは@provide, @injectデコレータで定義します。

@Provide(key?: string | symbol) 
@Inject(options?: { from?: InjectKey, default?: any } | InjectKey)

JavaScript

Parent.vue

<script>
export default {
  name: 'Parent',
  provide: {
    commonValue: 'foo'
  }
}
</script>

Child.vue

<script>
export default {
  name: 'Child',
  inject: {
    commonValue: { default: 'hoge' }
  }
}
</script>

TypeScript

Parent.vue

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

@Component()
export default class Parent extends Vue {
  @Provide() commonValue = 'foo';
}
</script>

Child.vue

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

@Component
export default class Child extends Vue {
  @Inject({ default: 'hoge' })
  readonly commonValue!: string;
}
</script>

mixins

コンポーネント間の共通処理を定義するミックスインはMixins()の継承を使って表現します。
ミックスイン自身もVueを継承したクラスとして定義します。
複数のミックスインを使う場合も、Mixins()の引数として与えることが可能です。
(最大5つ)
https://github.com/vuejs/vue-class-component/blob/master/src/util.ts#L35

JavaScript

mixinLogger.js

export default {
  created () {
    const now = new Date
    console.log(`created: ${now}`)
  },
  methods: {
    log (param) {
      console.log(`log: ${param}`)
    }
  }
}

Human.vue

<script>
import MixinLogger from './mixinLogger'

export default {
  name: 'Human',
  mixins: [MixinLogger],
  methods: {
    greeting () {
      console.log('おはよう')
      this.log('call greeting')
    }
  }
}
</script>

TypeScript

mixin.ts

import { Component, Vue } from 'vue-property-decorator';

@Component
export default class mixinLogger extends Vue {
  created() {
    const now = new Date;
    console.log(`created: ${now}`);
  }

  log(param) {
    console.log(`log: ${param}`);
  }
}

Human.vue

<script lang="ts">
import MixinLogger from './mixinLogger';
import { Component, Mixins } from 'vue-property-decorator';

@Component
export default class Human extends Mixins(MixinLogger) {
  greeting() {
    console.log('おはよう');
    this.log('call greeting');
  }
}
</script>

created, mounted, updated, destroyed ...etc

ライフサイクルフックはそのまま同名のメソッドとして宣言することですることで実装可能です。

JavaScript

<script>
export default {
  name: 'Human',
  created: function () {
    // 何か処理
  },
  mounted: function () {
    // 何か処理
  },
  updated: function () {
    // 何か処理
  },
  destroyed: function () {
    // 何か処理
  }
}
</script>

TypeScript

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

@Component
export default class Human extends Vue {
  created() {
    // 何か処理
  }

  mounted() {
    // 何か処理
  }

  updated() {
    // 何か処理
  }

  destroyed() {
    // 何か処理
  }
}
</script>

参考

https://github.com/vuejs/vue-class-component
https://github.com/kaorun343/vue-property-decorator

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away