This TypeScript error occurs when attempting to iterate over a value that does not implement the iterable protocol, such as using for...of on non-array types or union types containing non-iterable values.
This error indicates that you're trying to use iteration syntax (like `for...of` loops, spread operators `...`, or array destructuring) on a value that TypeScript doesn't recognize as iterable. In JavaScript and TypeScript, only certain types are iterable by default: arrays, strings, Maps, Sets, and objects that implement the `Symbol.iterator` method. The error message "Value of type 'X' is not iterable" means TypeScript has determined at compile-time that the value you're trying to iterate doesn't have the required `[Symbol.iterator]()` method. This commonly happens with union types where one or more possible types aren't iterable, plain objects (which aren't iterable by default), or when a value might be `undefined` or `null`. TypeScript's type system catches this error before runtime, preventing the JavaScript runtime error "TypeError: X is not iterable" that would otherwise crash your application.
If your value is a union type that includes undefined or null, add a type guard before iteration:
// ❌ Error: Value of type 'string[] | undefined' is not iterable
function processItems(items: string[] | undefined) {
for (const item of items) { // Error!
console.log(item);
}
}
// ✅ Fix: Check for undefined first
function processItems(items: string[] | undefined) {
if (!items) return;
for (const item of items) { // Safe now
console.log(item);
}
}
// ✅ Alternative: Use optional chaining with forEach
function processItems(items: string[] | undefined) {
items?.forEach(item => console.log(item));
}Type guards narrow the union type to exclude non-iterable variants.
Plain objects aren't iterable by default. Use Object.entries(), Object.keys(), or Object.values():
// ❌ Error: Type '{ name: string; age: number; }' is not iterable
const user = { name: 'Alice', age: 30 };
for (const prop of user) { // Error!
console.log(prop);
}
// ✅ Fix: Use Object.entries() for key-value pairs
for (const [key, value] of Object.entries(user)) {
console.log(key, value);
}
// ✅ Or use Object.keys() for just keys
for (const key of Object.keys(user)) {
console.log(key, user[key as keyof typeof user]);
}
// ✅ Or use Object.values() for just values
for (const value of Object.values(user)) {
console.log(value);
}Ensure your type annotations correctly reflect iterable types:
// ❌ Error: Incorrectly typed as object
function sumNumbers(nums: object) {
let sum = 0;
for (const num of nums) { // Error: object is not iterable
sum += num;
}
return sum;
}
// ✅ Fix: Type as array
function sumNumbers(nums: number[]) {
let sum = 0;
for (const num of nums) {
sum += num;
}
return sum;
}
// ✅ Or use generic iterable interface
function sumNumbers(nums: Iterable<number>) {
let sum = 0;
for (const num of nums) {
sum += num;
}
return sum;
}If you need to iterate but have a non-iterable type, convert it first:
// ❌ Error with Map or Set values
const userMap = new Map([['id1', 'Alice'], ['id2', 'Bob']]);
// Can't spread the map directly in some contexts
// ✅ Convert to array first
const entries = Array.from(userMap);
const ids = [...userMap.keys()];
const names = [...userMap.values()];
// ❌ Single value mistaken for iterable
const value: string | string[] = getSomeValue();
for (const char of value) { // Error if value is string[]
// ...
}
// ✅ Ensure it's always an array
const values = Array.isArray(value) ? value : [value];
for (const item of values) {
// ...
}If you need a custom object to be iterable, implement the Symbol.iterator method:
class NumberRange {
constructor(private start: number, private end: number) {}
// Implement Symbol.iterator
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
// ✅ Now iterable
const range = new NumberRange(1, 5);
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
const numbers = [...range]; // [1, 2, 3, 4, 5]If you're certain a value is iterable but TypeScript disagrees, use a type assertion:
// Only use when you're absolutely certain the runtime value is iterable
const items = unknownValue as Iterable<string>;
for (const item of items) {
console.log(item);
}
// Better: Combine with runtime check
if (typeof (unknownValue as any)[Symbol.iterator] === 'function') {
const items = unknownValue as Iterable<string>;
for (const item of items) {
console.log(item);
}
}Warning: Type assertions bypass TypeScript's safety checks. Only use when you have runtime guarantees the value is iterable.
ES5 vs ES6 Iteration Targets: When targeting ES5, TypeScript may generate different iteration code that can cause infinite loops if used on plain objects instead of throwing proper errors. Always target ES2015+ for proper iterable behavior or ensure you're only iterating arrays.
Async Iterables: For async iteration (using for await...of), ensure your type implements AsyncIterable<T> instead of Iterable<T>. Mixing these will cause type errors.
QueryList and Framework-Specific Issues: Some framework types like Angular's QueryList may not properly expose their iterable interface in type definitions. Check the framework documentation for proper iteration methods or convert to arrays with toArray().
readonly vs Mutable Arrays: Both readonly T[] and T[] are iterable, but if you're working with the Iterable<T> interface directly, be aware of covariance issues when the iterable yields mutable objects.
Performance Consideration: Object.entries(), Object.keys(), and Object.values() create new arrays, which has a memory cost for large objects. For performance-critical code, consider traditional for loops or check if the object can be restructured to use Maps or Sets which are natively iterable.
Function expression requires a return type
Function expression requires a return type
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript
Type 'X' is not assignable to inferred conditional type
How to fix "Type not assignable to inferred conditional" in TypeScript