業務でVue(Nuxt).jsを書きはじめて3ヶ月ほど経った。 それまではReact + TSのプロジェクトだったので、TSの良さは分かっているので、 今のプロジェクトにもTSを導入しようと思っていたが、どうやらVueのTS事情は複雑らしい。 Vueでプロダクトを作る場合、VuexやVue-Routerを使うことが多いが、その中で型の恩恵を受けにくかったり、そもそも書き方が複数あるようだ。 しかもVue3のリリースで、また事情が変わるらしい。
ちょっと時間ができたので、Vue3リリース前のVue + TS事情を調べてまとめておく。 今回はTSを使うためのコードの書き方と、型情報がどのように使用できるかに焦点を当てる。
VueとTypeScriptの今
2020年5月10日現在、Vue v3はベータ版であり、Vue v2.xが最新の公式となる。
この状態でコードの書き方は2通りある。
公式ドキュメント)に全てが書いてあるが、
Vue.extend
を使った方法と、 vue-class-component
を使った方法だ。それぞれでかなり書き方が違う。
例えば、こんなVue.jsのcomponentがある場合
<template> <div class="hello"> <h1>{{ msg }}</h1> <p>{{ saySomething() }}</p> </div> </template> <script> export default { name: "HelloWorld", props: { msg: { type: String }, }, methods: { saySomething() { const something = "something"; return something; }, }, }; </script>
Vue.extend
を使うとこうなり
<template> <div class="hello"> <h1>{{ msg }}</h1> <p>{{ saySomething() }}</p> </div> </template> <script lang="ts"> import Vue from "vue"; export default Vue.extend({ name: "HelloWorld", props: { msg: { type: String }, }, methods: { saySomething(): string { const something = "something"; return something; }, }, }); </script>
vue-class-component
を使うとこうなる
<template> <div class="hello"> <h1>{{ messsag }}</h1> <p>{{ saySomething() }}</p> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator"; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; saySomething(): string { const something = "something"; return something; } } </script>
Vue.extend
では、componentの宣言でVueクラスを拡張し、内部の関数などの返り値の型を記述するだけでよいので、通常の書き方に近い。
vue-class-componen)はそもそも、Vueの記述をclass-style−syntaxで記述するために用意されているAPIであり、コードの記述が結構変わる。
vue-class-component
はこれからも開発されていくようだが、Vueのv3にそのようなclass-style-syntaxでcomponentを定義するための提案がされていたがhttps://github.com/vuejs/rfcs/pull/17
却下されてしまった。
これを受けて、 Vue.extend
で記述するケースが多くなっているようだ。
また、どちらのパターンでもpropsにTSの型推論を聞かせる
また、Vuexとかmixin使うときには型が自動で解決されないので、手動で型宣言をする必要があるようだ。これについては以下のLINE UITブログがわかりやすかった。
https://engineering.linecorp.com/ja/blog/vue-js-typescript-otoshidama/
Vue v3とTypeScript
前述したcomponentをVue3(Composition API)で書くと、以下のようになる。
<template> <div class="hello"> <h1>{{ msg }}</h1> <p>{{ saySomething() }}</p> </div> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ props: { msg: { type: String } }, setup() { const saySomething = (): string => { const something = "something"; return something; }; return { saySomething }; } }); </script>
まあこれはv3でTSを使うための変更というより、単にcompositionAPIを使ってcomponentを定義する書き方の変更のほうが大きい。
composition APIについては公式docsを参照 https://composition-api.vuejs.org/#summary
もしこの書き方でpropsにTSによる型推論を聞かせてsetup内で使いたい場合は
type Props = { msg: string; };
みたいな型をつくり
... setup(props: Props) { ...
こんな感じで型を当てる必要がある。
vuexとかvue-routerはcomposition APIに対応中で、docsはまとまっていなかった・・・w
https://github.com/vuejs/vuex/tree/4.0 https://github.com/vuejs/vue-router-next
気になったらソースコードを見てみよう。
なんなら今のうちにソースコードを追っかけてればdocument自分でかけるかもしれない。
まとめ
以上触れたとおり、
- Vue 2.xではTSの書き方は大きく2種類ある。
- そして、vuexなどを用いるときは型推論が自動できかないので、自分で型を指定する必要がある。
- Vue 3からはcompositionAPIの導入によりさらに書き方が変わる。各ライブラリは鋭意対応中。
って感じ。
あとの詳しいところは実際に使ってみながら調べるかなー。