前提
今まではVueとTypeScriptの組み合わせだと vue-class-component
を使うというのが定石だったけど、Vue 2.5ではコンポーネントの型定義が大きく改善されたおかげで、TypeScriptでも vue-class-component
を使わずに普通の方法でコンポーネントを書けるようになった。
つまり、こんな感じで書いたとすると、各functionの中でちゃんと this.foo
、this.bar
、this.baz
にアクセスすることができる。(もちろん、それぞれの型も正しく認識される)
import Vue, { VNode } from "vue";
export const MyComponent = Vue.extend({
name: "MyComponent",
props: {
foo: String
},
computed: {
bar() { return this.foo.toUpperCase(); }
},
methods: {
baz() { console.log(this.bar); }
},
render(h): VNode {
return h("div", { on: { click: this.baz } }, this.foo);
}
});
vue-class-component
を使うなら、こんな感じになる。
import Vue from "vue";
import component from "vue-class-component";
@component({
props: {
foo: String
},
})
export class MyComponent extends Vue {
get bar() {
return this.foo.toUpperCase();
}
baz() {
console.log(this.bar);
}
render(h) {
return h("div", { on: { click: this.baz } }, this.foo);
}
}
本題
さて、props
、data
、methods
、computed
については型が導出されるようになったけど、ほかにもいろいろと型をつけたい部分が出てくることがある。
例えばVuexを使うなら MyStore
型の $store
をメンバに追加したいと思うかもしれない。
もしくは、テンプレート内のdiv要素に ref="box"
を指定しているなら、 $refs
の型が { box: HTMLDivElement }
であることを明示したいと思うかもしれない。
他にも、 $scopedSlots
のスロット名や引数を型を明示したいとかもある。
vue-class-component
を使っていればこういうのは単にメンバーを追加すればよかった。
import Vue from "vue";
import component from "vue-class-component";
import { MyStore } from "./store";
@component({ /* 略 */ })
export class MyComponent extends Vue {
$store: MyStore;
$refs: {
box: HTMLDivElement
}
/* 以下略 */
}
じゃあ Vue.extend
で同じことがしたければどうすればいいの?という話
解決策
こんな感じのメソッドを用意する
import Vue from "vue";
import { VueConstructor } from "vue/types/vue";
export function vueWith<T>(): VueConstructor<Vue & T> {
return Vue as any;
}
で、こう
import Vue, { VNode } from "vue";
import { MyStore } from "./store";
export const MyComponent = vueWith<{
$store: MyStore,
$refs: { box: HTMLDivElement }
}>().extend({
name: "MyComponent",
methods: {
onClick() {
console.log($refs.box.innerHTML);
}
}
render(h): VNode {
return h("div", { ref: "box", on: { click: this.onClick } }, this.$store.state.message);
}
});
もっといい方法があれば教えてください。