HTMLCollectionOfでfor-ofループが使えない時の解決法

公開日:
目次

TypeScriptでHTMLCollectionを扱う際に、以下のようなエラーに遭遇したことはありませんか?

Type 'HTMLCollectionOf<HTMLMetaElement>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.

開発環境では問題なく動作するのに、本番ビルドでだけエラーになる厄介な問題です。この記事では、簡単で確実な解決方法を紹介します。

問題のコード

以下のようなコードでエラーが発生します:

const metas = document.getElementsByTagName('meta')
for (const meta of metas) {  // ← ここでエラー
  const name = meta.getAttribute('name')
  // 処理...
}

エラーメッセージが示すように、HTMLCollectionOf<HTMLElement>for...ofループで直接反復処理できません。

解決方法

最も簡単で確実な解決方法は、従来のインデックスベースのループに書き換えることです:

const metas = document.getElementsByTagName('meta')
for (let i = 0; i < metas.length; i++) {
  const meta = metas[i]
  const name = meta.getAttribute('name')
  // 処理...
}

修正前後の比較

// ❌ エラーになるコード
for (const meta of metas) {
  const np = meta.getAttribute('name') || meta.getAttribute('property')
  if (typeof np !== 'string') continue
}

// ✅ 修正後のコード
for (let i = 0; i < metas.length; i++) {
  const meta = metas[i]
  const np = meta.getAttribute('name') || meta.getAttribute('property')
  if (typeof np !== 'string') continue
}

他の解決方法

方法1: Array.fromを使用

const metas = document.getElementsByTagName('meta')
for (const meta of Array.from(metas)) {
  // 処理...
}

方法2: スプレッド演算子を使用

const metas = document.getElementsByTagName('meta')
for (const meta of [...metas]) {
  // 処理...
}

方法3: Array.prototype.forEachを使用

const metas = document.getElementsByTagName('meta')
Array.prototype.forEach.call(metas, (meta) => {
  // 処理...
})

なぜ開発環境では動くのか

開発環境と本番環境でTypeScriptのコンパイル設定が異なるためです:

  • 開発環境: より新しいES仕様でコンパイル
  • 本番環境: 互換性のためにES5などの古い仕様でコンパイル

この違いにより、HTMLCollectionOfの反復処理の挙動が変わります。

推奨する解決方法

実際のプロジェクトでは、インデックスベースのループを推奨します:

for (let i = 0; i < collection.length; i++) {
  const element = collection[i]
  // 処理...
}

理由

  1. 互換性が高い: どのES仕様でも動作
  2. パフォーマンス: 配列変換のオーバーヘッドがない
  3. シンプル: 追加のメソッド呼び出しが不要
  4. デバッグしやすい: インデックスが分かりやすい

実際の修正例

私のプロジェクトでの実際の修正:

// lib/api.ts での修正
export const getMetaData = async (url: string) => {
  const res = await fetch(url)
  const text = await res.text()
  const doms = new JSDOM(text)
  const metas = doms.window.document.getElementsByTagName('meta')
  
  // 修正前: for (const meta of metas)
  for (let i = 0; i < metas.length; i++) {
    const meta = metas[i]
    const np = meta.getAttribute('name') || meta.getAttribute('property')
    if (typeof np !== 'string') continue
    
    if (np.match(/title/)) {
      // タイトル処理...
    }
  }
}

まとめ

HTMLCollectionOffor...ofループエラーが出た場合:

  1. インデックスベースのループに書き換える(推奨)
  2. 必要に応じてArray.from()で配列に変換
  3. TypeScript設定の見直しも検討

この修正により、開発環境と本番環境で一貫した動作を保証できます。シンプルで確実な解決方法なので、同じエラーに遭遇した際はぜひ試してみてください。