Vue.jsは素のwebページ用コード(HTML, JavaScript, CSS)の雰囲気を保ったままいい感じに動いてくれるJavaScriptフレームワークです。特に単一ファイルコンポーネントはHTMLをHTMLのまま、VueオブジェクトをVueオブジェクトのまま、CSSをCSSのまま表現してまとまったデザインのコンポーネントを作り上げます。
<template> <p>{{ greeting }} World!</p> </template> <script> module.exports = { data: function () { return { greeting: 'Hello' } } } </script> <style scoped> p { font-size: 2em; text-align: center; } </style>
単一ファイルコンポーネント — Vue.js
これは便利なのですが、TypeScriptとはどうにも相性が悪いです。少なくとも2点問題点がありそれはJavaScriptコード部で定義された値をテンプレートに当てはめた時に型定義を持ち越すのが難しい点とJavaScript部の定義がほとんどが素朴なオブジェクトのためTypeScriptの型構文を記述する余地が少ない点です。
上記の問題を解決する方法の一つとしてvue-property-decoratorがあります。下記の引用の様にclass構文と@以下によるデコレートでVueコンポーネントであることを表現します。
@Component class Test extends Vue { @Prop(Number) [propertyName]!: number // @Propでpropsの一つであることを表現 }
kaorun343/vue-property-decorator: Vue.js and Property Decorator
これはこれでありですし言語実装としては簡単(何がどこに現れるのか、どこからどこまでが区切りなのか厳密)なのでしょうが、Vue.jsからいくらか離れた記法を用いる点でIDE、ESlint、storybookなどと組み合わせることと機能の把握に面倒が起きます。こうなるならばReactを使って新たにJavaScriptでまとめ切る方が便利な気がします。
vue-property-decoratorの難点を解決する方法としてVue.extend()というやり方があります。これはvue-property-decoratorからいくらか後の実装で追加されたようです(また聞きで一次ソース未確認)。
API — Vue.js#Vue-extend
このやり方とTypeScriptを組み合わせると次のように記述できます。素の状態では素朴なオブジェクトしか返せませんでしたがVue.extendの引数としての型であるComponentOptionsを得ました。
<template> <p>{{ greetingToTarget }}</p> </template> <script lang="ts"> import Vue, {ComponentOptions, PropType} from 'vue'; type HelloWorldDataType = { greeting: string, modalLevel: string, } export default Vue.extend({ props: { target: {type: String as PropType<string>, required: true}, }, data: function (): HelloWorldDataType { return { greeting: 'Hello' // data全体の返り値の型を決めるのでなく、ここでas stringとするのも手 } }, computed: { greetingToTarget():string { return `${this.greeting} ${this.target}!`; } }, } as ComponentOptions); </script> <style scoped> p { font-size: 2em; text-align: center; } </style>
上述のようにするとIDEの型に関する機能や予測補完が強く働きだし便利な上、想定外の挙動をした時には厳しく警告を飛ばしてくれます。
webpackのプロダクト用変換などの方法で純粋なJavaScriptに圧縮変換するとなんやかんや動くJavaScriptの特徴も復活させられます。