LoginSignup
7
7

More than 5 years have passed since last update.

Vue 2.5 のコンポーネントのメンバーに型を追加する

Posted at

前提

今まではVueとTypeScriptの組み合わせだと vue-class-component を使うというのが定石だったけど、Vue 2.5ではコンポーネントの型定義が大きく改善されたおかげで、TypeScriptでも vue-class-component を使わずに普通の方法でコンポーネントを書けるようになった。

やってくるVue2.5でのTypeScriptの変更

つまり、こんな感じで書いたとすると、各functionの中でちゃんと this.foothis.barthis.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);
  }
}

本題

さて、propsdatamethodscomputed については型が導出されるようになったけど、ほかにもいろいろと型をつけたい部分が出てくることがある。

例えば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);
  }
});

もっといい方法があれば教えてください。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7