TypeError: X is not iterable とは
JavaScriptで `TypeError: X is not iterable` というエラーに遭遇しましたか?このエラーは、`for…of` ループやスプレッド構文など、反復可能な(iterable)オブジェクトを期待する場所で、そうではない値(`null`, `undefined`, オブジェクトなど)を渡したときに発生します。特に、APIからのレスポンスや外部データの処理でよく見かけるエラーです。
エラーの発生パターン
このエラーは主に以下のようなケースで発生します。
パターン1: `null` または `undefined` を反復しようとした場合
// APIからのレスポンスがまだ来ていない、またはエラーでundefined/nullの場合を想定
let data = undefined; // または null
try {
// dataが配列であることを期待しているが、実際はundefined
for (const item of data) { // ここで TypeError: undefined is not iterable
console.log(item);
}
} catch (error) {
console.error(error.message);
}
for...of ループは、イテラブルなオブジェクト(配列、文字列、Map、Setなど)に対してのみ使用できます。undefined や null はイテラブルではないため、このエラーが発生します。APIレスポンスの遅延や失敗で変数が未初期化のままになっているケースが多いです。
// dataがundefined/nullの場合に備えてデフォルト値(空の配列)を設定
let data = undefined; // または null
try {
// dataがundefined/nullの場合は空の配列を使用する
for (const item of data || []) {
console.log(item);
}
// または明示的にチェックする
if (data && typeof data[Symbol.iterator] === 'function') {
for (const item of data) {
console.log(item);
}
} else {
console.warn('Data is not iterable or is null/undefined.');
}
} catch (error) {
console.error(error.message);
}
パターン2: プレーンなオブジェクトを反復しようとした場合
const user = {
id: 1,
name: 'Taro',
email: 'taro@example.com'
};
// オブジェクトを直接 for...of で反復しようとするとエラー
for (const prop of user) { // TypeError: user is not iterable
console.log(prop);
}
JavaScriptのプレーンなオブジェクト({} で作成されるオブジェクト)は、デフォルトではイテラブルではありません。したがって、for...of ループやスプレッド構文で直接反復しようとするとこのエラーが発生します。オブジェクトのプロパティを反復したい場合は、Object.keys(), Object.values(), Object.entries() などのメソッドを使用する必要があります。
const user = {
id: 1,
name: 'Taro',
email: 'taro@example.com'
};
// オブジェクトのキーを反復
for (const key of Object.keys(user)) {
console.log(`Key: ${key}, Value: ${user[key]}`);
}
// オブジェクトの値を反復
for (const value of Object.values(user)) {
console.log(`Value: ${value}`);
}
// オブジェクトのキーと値のペアを反復
for (const [key, value] of Object.entries(user)) {
console.log(`Key: ${key}, Value: ${value}`);
}
パターン3: 数値や真偽値などのプリミティブ型を反復しようとした場合
const count = 5;
// 数値を直接 for...of で反復しようとするとエラー
for (const i of count) { // TypeError: count is not iterable
console.log(i);
}
const isActive = true;
// 真偽値を直接 for...of で反復しようとするとエラー
for (const value of isActive) { // TypeError: isActive is not iterable
console.log(value);
}
数値、真偽値、シンボルなどはイテラブルではありません。これらのプリミティブ型を for...of で直接反復しようとするとエラーになります。これらの値を使って反復処理を行いたい場合は、まず配列などのイテラブルな型に変換する必要があります。
const count = 5;
// 数値から配列を作成して反復
for (const i of Array.from({ length: count }, (_, idx) => idx + 1)) {
console.log(i); // 1, 2, 3, 4, 5
}
const isActive = true;
// 真偽値を使って条件分岐
if (isActive) {
console.log('Active state is true.');
} else {
console.log('Active state is false.');
}
// または、真偽値を要素とする配列を作成して反復(稀なケース)
for (const value of [isActive]) {
console.log(`Value: ${value}`);
}
根本原因の特定方法
エラーが発生した行にブレークポイントを設定し、デバッガで対象の変数の値と型を確認しましょう。`console.log()` で変数を直接出力するのも有効です。特に `typeof` 演算子や `Array.isArray()` メソッドを使って、期待する型と実際の型が一致しているかを確認してください。
```javascript
let problematicData = undefined; // あるいは null, { key: 'value' }, 5 など
console.log('--- Debugging problematicData ---');
console.log('Value:', problematicData);
console.log('Typeof:', typeof problematicData);
console.log('Is Array:', Array.isArray(problematicData));
console.log('Has Symbol.iterator:', typeof problematicData?.[Symbol.iterator] === 'function');
console.log('------------------------------');
// エラーが発生するコード(コメントアウトしてデバッグ情報を確認)
// for (const item of problematicData) {
// console.log(item);
// }
```
防止策とベストプラクティス
このエラーを防ぐには、{marker}イテレーションを行う前に変数の型や値が存在するかを常にチェックする{/marker}習慣をつけましょう。特にAPIレスポンスや外部入力など、予測不能なデータソースからの値には、デフォルト値を設定するか、条件分岐で処理をガードするコードを記述することが重要です。
```javascript
function processItems(items) {
// 1. デフォルト値の利用 (より推奨されるパターン)
const safeItems = items || []; // items が null/undefined の場合は空配列に
for (const item of safeItems) {
console.log(`Processing item: ${item}`);
}
// 2. 明示的な型チェックと条件分岐
if (Array.isArray(items)) {
for (const item of items) {
console.log(`Processing item: ${item}`);
}
} else {
console.warn('Warning: Expected an array, but received:', items);
// エラーハンドリングやログ記録
}
}
processItems(['a', 'b']);
processItems(null);
processItems(undefined);
processItems({ id: 1 }); // Warning: Expected an array, but received: { id: 1 }
```
よくある質問(FAQ)
-
Qなぜ `for…in` だと `TypeError: X is not iterable` が発生しないのですか?
-
A
`for…in` ループは、オブジェクトの列挙可能なプロパティのキーを反復するために設計されています。これはイテラブルプロトコルを使用せず、オブジェクトのプロパティを直接扱います。そのため、イテラブルではないオブジェクトに対してもエラーなしで動作します。ただし、`for…in` はプロトタイプチェーン上のプロパティも列挙するため、`hasOwnProperty` でフィルタリングするなどの注意が必要です。
-
QJavaScriptのプレーンなオブジェクト(`{}`)を `for…of` で反復したい場合はどうすれば良いですか?
-
A
プレーンなオブジェクトを直接 `for…of` で反復することはできません。代わりに `Object.keys()`、`Object.values()`、`Object.entries()` メソッドを使って、オブジェクトのキー、値、またはキーと値のペアの配列を取得し、その配列に対して `for…of` を使用します。例:`for (const [key, value] of Object.entries(myObject))`。
-
QAPIからのレスポンスでこのエラーが発生した場合の効率的な対処法は?
-
A
APIレスポンスは非同期であり、`null`や`undefined`が返される可能性があります。データを利用する前に、Optional Chaining (`?.`) や Nullish Coalescing (`??`) 演算子を使って、安全にアクセスできるようにしましょう。また、`if (response && Array.isArray(response.data))` のように明示的にチェックすることも有効です。Reactの`useState`のように、初期値を空の配列で設定するのも良い方法です。
-
QこのエラーをLinterやTypeScriptで事前に防止できますか?
-
A
はい、可能です。ESLintのようなLinterは、潜在的な`null`や`undefined`へのアクセスを警告するルール(例: `@typescript-eslint/no-unsafe-member-access`)を提供しています。TypeScriptを使用すると、型定義によって変数がイテラブルであるか、または`null`/`undefined`の可能性があるかをコンパイル時にチェックできるため、未然にエラーを防ぐ非常に強力な手段となります。
-
Q本番環境でだけ `TypeError: X is not iterable` が発生するケースはありますか?
-
A
はい、あります。開発環境と本番環境でAPIのレスポンス形式が異なる場合(例: 開発中はダミーデータで配列だが、本番ではエラー時に`null`や空のオブジェクトが返される)、CDNのキャッシュの有無、または特定のユーザー環境(古いブラウザやネットワーク環境)でのみ発生することがあります。本番環境でのログ監視やエラー報告ツール(Sentryなど)を利用して、実際のデータの形やエラー発生時のコンテキストを把握することが重要です。
この用語と一緒に知っておきたい用語
| 用語 | この記事との関連 |
|---|---|
| NULL | このエラーは`null`や`undefined`がイテラブルでないために発生することが多いため。 |
| デバッガ | エラーの原因となっている変数の型や値を確認するために必須のツールであるため。 |
| スニペット | 安全なイテレーション処理の共通コード例として、スニペットが役立つため。 |
| アルゴリズム | データの反復処理のロジックを設計する上で、イテラブルな性質の理解が重要であるため。 |
| 予約語 | JavaScriptの`for…of`のような構文を正しく理解することがエラー解決に繋がるため。 |


コメント