（vue.js + typescript）型付きでデータをコンポーネントに渡す簡単なデモ


## 目標：コンポーネント層に注入するデータを型指定したい
より抽象的なコンポーネントの呼び出しを考え、データの注入時に、コンポーネントの props に渡せるデータに型を指定できるようにしたいと思います。

下記の都道府県のセレクトタグのコンポーネントに渡すOptionのデータを親から子コンポーネントへ渡す際に、型を指定するのが目標です。

```取得できる予定の都道府県のデータ.json
{
  "id": 1,
  "name": "東京都"
}, {
  "id": 2,
  "name": "それ以外"
}
```
このデータ自体には型がないので、typescript でこのデータ用の型を作り、受け取る際に型を使用したいと思います。

## 0. 環境設定（vue-cliのtypescript使用時のデフォルトの設定）
vue-cli が typescript を使用する選択した際にセットアップするデフォルトの環境設定を使用します。（@nrslib さんのvue.tsの記事、また吉井健文さんの「実践TypeScript ~	BFFとNext.js&Nuxt.jsの型定義~」を参考にさせて頂きました。）
https://qiita.com/nrslib/items/be90cc19fa3122266fd7
[「実践TypeScript ~	BFFとNext.js&Nuxt.jsの型定義~」](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5TypeScript-BFF%E3%81%A8Next-js-Nuxt-js%E3%81%AE%E5%9E%8B%E5%AE%9A%E7%BE%A9-%E5%90%89%E4%BA%95-%E5%81%A5%E6%96%87/dp/483996937X)

## 1. 型（interface、class）の実装
カスタムコンポーネント「MyOption.vue」に渡すデータの定義としてinterface「MyOptionInterface」を実装します。

```src/models/MyOption/MyOptionInterface.ts
export default interface MyOptionInterface {
  id: number;
  name: string;
}
```

併せて、Prefecture.tsにMyOptionInterfaceを実装した、都道府県のデータを渡すためのinterface「Prefecture」を実装します。

```src/models/MyOption/Prefecture.ts
import MyOptionInterface from "./MyOptionInterface";
export default class Prefecture implements MyOptionInterface {
  id: number;
  name: string;
  constructor(p__id: number, p__name: string) {
    this.id = p__id;
    this.name = p__name;
  }
}
```

## 2. データ取得時の型判別
-  `getPrefectures()` の戻り値に、先ほど作成した「MyOptionInterface」を実装したクラス「Prefecture」の配列、という型を指定します。
- data の 変数 `prefectures` に、「Prefecture」の配列、という型であることを指定します。

```src/App.vue
<template>
  <div id="app">
    <h2>アンケート</h2>
    <p>アンケートにご協力ください</p>
    <form v-on:submit.prevent>
      <MyOption
        text="あなたの住んでいる都道府県は？"
        id="prefecture"
        :myoptions="prefectures"
        :selectedValue="{id:1}"
      ></MyOption>
      <button @click="gatherResult">send</button>
    </form>
    <div v-if=" this.result.length !== 0 ">{{ result }}</div>
  </div>
</template>

<script lang='ts'>

import MyOption from "./components/MyOption.vue";
import Prefecture from "./models/MyOption/Prefecture";
import { Vue } from "vue-property-decorator";

function getPrefectures():Prefecture[]{
  return [{
      "id": 1,
      "name": "東京都"
    }, {
      "id": 2,
      "name": "それ以外"
    }];
};


export default Vue.extend({
  name: "App",
  data():{
    result:Array<any>
    prefectures:Prefecture[]
  }
  {
    return {
      result: [],
      prefectures : []
    };
  },
  created(){
    this.prefectures = getPrefectures();
  },
  methods: {
    gatherResult():void {
      this.result = [];
      this.result.push(
        (<HTMLInputElement>document.getElementById('prefecture')).value
      );
    }
  },
  components: {
    MyOption
  }
});
</script>

```

## 3. コンポーネント間のデータ受渡しの実装
- props の 変数  `myoptions` に、先ほど作成した「MyOptionInterface」の配列、という型を指定します。


```src/components/MyOption.vue
<template>
  <p>
    <label :for="id" v-if=" text !== '' ">{{ text }}</label>
    <select :id="id">
      <option
        v-for="myoption in myoptions"
        :key="myoption.id"
        :value="myoption.id"
        :selected="myoption.id === selectedValue.id"
      >{{ myoption.name }}</option>
    </select>
  </p>
</template>

<script lang='ts'>
import { Vue } from "vue-property-decorator";
import MyOptionInterface from "./../models/MyOption/MyOptionInterface";

export default Vue.extend({
  props: {
    text: {
      type: String
    },
    id: {
      type: String,
      required: true
    },
    myoptions: {
      type: Array as () => MyOptionInterface[],
      required: true
    },
    selectedValue: {
      type: Object
    }
  }
});
</script>

```

props の変数への型指定の際、interfaceを指定する際は、先にArrayとして受け取り、MyOptionInterfaceの配列にcastするという形で記載する様でした。（classは直接型指定できる。）

## 実際の画面
画面は特に変わらないのですがクラスに渡されるデータが違うと、コーディング時にErrorを表示してくれるようになりました。


![VueType1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/d0d4ebf5-be89-5960-7402-51949524dfa7.png)

![VueType2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/d0b7edbe-1598-385d-6e77-fe39c99dbd33.png)



ソースコード
https://github.com/ttn1129/VueTypescriptSample




## その他
- typescript は tsconfig.json にかなり依存する。そのため、フレームワークが設定してくれるものを使う方が時間が割かれない。（自分で設定しようとして、かなりハマったため。。）
- props の変数への型指定の際、interfaceを指定する際は、先にArrayとして受け取り、MyOptionInterfaceの配列にcastするという形で記載しないといけない。
- 最初 codesandbox というサイトの vue-cli プロジェクトにpackageを自分で追加してコーディングしていたのですが、なぜか`shims-vue.d.ts` での `import vue from 'vue'`がEslintのParsing Errorになってしまう、というのがあり、結局同じソースコードをlocalでvue-cliプロジェクトとして`npm run serve`したらうまくいったので、コードの問題かと思ったのですが、codesandboxのvue-cliプロジェクトの環境の問題だったようでした。（vue-cliの普通のプロジェクトは全く問題なくうまくbuildできるのですが、typescript を使用したプロジェクトがbuildできずすごいハマりました。。）
- Vue の data への型の設定について下記のサイトを参照させていただきました。
https://qiita.com/kurosame/items/e56288e860cf016eaab0

- document.getElementByIdをtypescriptで行う際に、戻り値が`HTMLElement | null`という型指定があるらしく、valueを参照できなかったため、HTMLInputElementにcastする形でvalueを取得しました。
https://stackoverflow.com/questions/12989741/the-property-value-does-not-exist-on-value-of-type-htmlelement

- 型があるとコンポーネントに渡すデータを開発者が作りやすくなりそうだと思いました。

 
以上です。