Java

Why is an array not assignable to Iterable

27 September 2026 · 6 min read

Why is an array not assignable to Iterable

In the world of JavaScript and TypeScript, developers often encounter nuanced type-related challenges. One such head-scratcher that frequently surfaces, particularly in TypeScript, is understanding why an array is not assignable to Iterable directly. While arrays are inherently iterable in JavaScript, meaning you can loop over them with a for...of loop or use the spread syntax, their type definition in a strict type system like TypeScript reveals a subtle but critical distinction. This isn’t a limitation of JavaScript’s runtime behavior, but rather a reflection of how type systems categorize and enforce structure. Grasping this concept is fundamental for writing robust, type-safe code, especially when dealing with various data structures that support iteration.

Understanding Iterables and Arrays in JavaScript/TypeScript

To truly understand why an array is not assignable to Iterable, we first need to define what an “Iterable” is in the context of JavaScript and TypeScript. An object is considered iterable if it implements the iteration protocol. This protocol dictates that an object must have a method accessible via the constant Symbol.iterator. When called, this method should return an object known as an “iterator,” which itself must conform to the iterator protocol, providing a next() method that returns an object with value and done properties.

JavaScript arrays, by default, fulfill this iteration protocol. They have an intrinsic Symbol.iterator method, which is why you can effortlessly use for...of loops, the spread operator (...), and other iteration-based constructs with them. This built-in capability makes arrays one of the most common iterable types in the language. However, TypeScript’s type definition for Iterable is a more general interface, representing any object that adheres to this protocol, not just arrays. This distinction is crucial for type checking.

The TypeScript Iterable interface is typically defined as:

interface Iterable<T> { [Symbol.iterator](): Iterator<T>; } 

This interface declares that an object is iterable if it has a method named by Symbol.iterator that returns an Iterator<T>. While arrays implicitly satisfy this contract at runtime, TypeScript’s type system often treats specific types (like Array<T>) and general interfaces (like Iterable<T>) as distinct, even if one structurally matches the other. This strictness helps prevent subtle bugs by ensuring that only explicitly compatible types are used where an Iterable is expected.

The Core Discrepancy: Symbol.iterator

The primary reason an array is not directly assignable to Iterable in a strict type context like TypeScript lies in the subtle difference in how their types are perceived, even though arrays are fundamentally iterable. While an array inherently possesses a Symbol.iterator method, making it iterable at runtime, TypeScript’s type definition for Array<T> does not explicitly extend the Iterable<T> interface. Instead, Array<T> is a concrete class with many additional properties and methods (like push, pop, map, filter) that the simpler Iterable<T> interface does not declare. This means that an Array<T> is a more specific type than Iterable<T>, and while an Array<T> is an Iterable<T> in practice, TypeScript requires explicit type compatibility for assignments.

Consider a function that expects an Iterable<string>:

function processIterable(data: Iterable<string>) { for (const item of data) { console.log(item.toUpperCase()); } } const myArray: string[] = ["hello", "world"]; // processIterable(myArray); // This might cause a type error depending on TypeScript version and strictness 

In older TypeScript versions or under certain strictness settings, passing myArray directly to processIterable might trigger a type error. This is because TypeScript’s structural type system, while powerful, sometimes requires explicit acknowledgment when a more specific type (like string[]) is being used in a context expecting a more general interface (like Iterable<string>). The array type includes properties that the Iterable interface doesn’t know about, creating a potential mismatch in the type checker’s eyes. As noted by the TypeScript Handbook, “iterables are objects which implement the Symbol.iterator method.” Arrays do this, but their full type signature is richer.

This strictness is a design choice aimed at catching potential errors early. If a function expects only the iteration capabilities (i.e., Iterable), passing an array, which has many other methods, might obscure the true intent or lead to unintended usage of array-specific methods where only generic iteration is desired. The Iterable interface ensures that only the iteration contract is guaranteed, promoting more robust and predictable code when working with diverse data sources, such as custom iterators or generator functions.

Bridging the Gap: Making Arrays and Array-likes Iterable

While TypeScript might enforce a distinction, there are straightforward ways to explicitly treat an array as an Iterable or convert other array-like objects into proper arrays that are naturally iterable. These methods leverage JavaScript’s built-in functionalities and TypeScript’s type assertions to ensure compatibility.

  1. Type Assertion: The simplest way to satisfy TypeScript is to use a type assertion, telling the compiler that you know the array is indeed an Iterable. ``` const myArray: string[] = [“alpha”, “beta”]; const myIterable: Iterable = myArray as Iterable; // Now ‘myIterable’ can be used where Iterable is expected without error
    
    This approach bypasses the type checker's strictness, which should be used judiciously, only when you are certain the underlying object fulfills the interface.
    
  2. Using Array.from(): For array-like objects or other iterables that aren’t explicitly arrays, Array.from() is a powerful method to create a new, shallow-copied Array instance from them. Since arrays are inherently iterable, the result is perfectly compatible. ``` function processArrayLike(data: ArrayLike) { const actualArray = Array.from(data); // ‘actualArray’ is now string[] // … process ‘actualArray’ as needed }
    
    `Array.from()` can convert anything that's iterable or array-like (has a `length` property and indexed elements) into a true array. This is especially useful for DOM collections (like `NodeList`) or `arguments` objects in functions.
    
  3. The Spread Operator (...): Similar to Array.from(), the spread operator can be used to convert an iterable into an array literal. This creates a new array containing all elements from the iterable. ``` const mySet = new Set([“one”, “two”, “three”]); const arrayFromSet = [… Question & Answer :

    with Java5 we can write:

    Foo[] foos = … for (Foo foo : foos)

    or just using an Iterable in the for loop. This is very handy.

    However you can’t write a generic method for iterable like this:

    public void bar(Iterable foos) { .. }

    and calling it with an array since it is not an Iterable:

    Foo[] foos = { .. }; bar(foos); // compile time error

    I’m wondering about the reasons behind this design decision.



    Arrays can implement interfaces (Cloneable and java.io.Serializable). So why not Iterable? I guess Iterable forces adding an iterator method, and arrays don’t implement methods. char[] doesn’t even override toString. Anyway, arrays of references should be considered less than ideal - use Lists. As dfa comments, Arrays.asList will do the conversion for you, explicitly.

    (Having said that, you can call clone on arrays.)