LoginSignup
7
5

More than 5 years have passed since last update.

Vue.js で TypeScript の Map 型の中身をテンプレートで v-for するには素の JS と違い Array.from が必要

Last updated at Posted at 2018-11-04

結論

Vue.js を TypeScript で書いたときにMap型の中身を列挙する方法に少しハマったので調べました。
結論としてはArray.fromMapArrayに変換する方法で列挙できます。

調べた理由

Vue.js で TypeScript のMap型の中身をテンプレートで列挙する際、
コンパイルエラーは起こさず以下の方法ではMapの中身が表示されませんでした。

<template>
  <div id="app">
    <ul>
      <li v-for="[key, value] in greetMap" :key="key">
        key: {{ key }} value: {{ value }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  private greetMap = new Map([
    [1, 'おはよう'],
    [2, 'こんにちは'],
    [3, 'おやすみ']
  ]);
}
</script>

Chorme のデベロッパーツールを確認すると以下のように表示されています。

image.png

JavaScriptを有効にしないとマップの反復が正しく機能しません。 有効にしてください。

JavaScript は有効になっているので、エラーメッセージが直接の原因ではないようで、
調べてみると JavaScript と TypeScript の仕様の違いが原因でした。

例えば以下のMap<number, string>のインスタンスをfor ofでそのまま列挙しようとすると、
JavaScript では OK ですが、TypeScript ではコンパイルエラーを起こします。

const greetMap = new Map([
  [1, 'おはよう'],
  [2, 'こんにちは'],
  [3, 'おやすみ']
])
JavaScriptではOKだがTypeScriptではコンパイルエラー!
for (const [key, value] of greetMap) {  // <- エラー
  console.log(`key: ${key} value: ${value}`)
}

↓↓ エラー内容 ↓↓
image.png

タイプ 'Map<number, string>'は配列型または文字列型ではありません。
イテレータの反復を許可するには、コンパイラオプション '--downlevelIteration'を使用します。

StackOverFlow に似た質問を見つけました。
Iterating over Typescript Map

TypeScript でMap型を列挙する場合、以下の方法でできるそうです。

  • Array.fromMapをネストした配列に変換する Map -> [[], [], []]
for (const [key, value] of Array.from(greetMap)) {
  console.log(`key: ${key} value: ${value}`)
}
  • Map.prototype.forEach
greetMap.forEach((value, key) => {
  console.log(`key: ${key} value: ${value}`)
})
  • "downlevelIteration": true + for of
tsconfig.json
{
  "downlevelIteration": true,
}
for (const [key, value] of greetMap) {
  console.log(`key: ${key} value: ${value}`)
}

Map型の中身をテンプレートでv-forする

Vue のテンプレート内部の式はほぼ JavaScript と互換性を持っているのでArray.fromする方法で表示ができました。

<template>
  <div id="app">
    <ul>
      <!-- Array.fromでネストした配列に変換 -->
      <li v-for="[key, value] in Array.from(greetMap)" :key="key">
        key: {{ key }} value: {{ value }}
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  private greetMap = new Map([
    [1, 'おはよう'],
    [2, 'こんにちは'],
    [3, 'おやすみ']
  ]);
}
</script>

image.png

Angular や React と違い、Vue のテンプレートの型チェックができない弱点により問題解決にちょっとだけ調査が必要でした。
Angular lnguage service や React の tsx の静的型付けとの親和性は高さが羨ましくもありますが、Vue も 3.0 から TypeScript との統合が進んでいくそうなので、テンプレートの型チェックできる未来も近いかもしれません!:grinning:

7
5
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
5