TypeScript 2.8.3 Type must have a Symbol.iterator method that returns an iterator
TypescriptTypescript Problem Overview
I am running into an error that says
Type must have a '[Symbol.iterator]()' method that returns an iterator.
It hopes on the demarcated line:
class Test {
private async do() {
const done = [...(await this.test())]; // Here's the error
}
private async *test(): AsyncIterableIterator<string> {
return;
}
}
I have found a few issues in the TypeScript GitHub repository, but none seem to have helped. They all suggest adding new entries to lib
. I am using es6
target and have added esnext
, dom
and es2018
. This has had zero effect on the error.
Do I miss more lib
entries (which I doubt as the ones I am using are the catchall ones with everything in) or is the code I am using invalid?
Typescript Solutions
Solution 1 - Typescript
this has helped me. in tsconfig, add this :
{
"compilerOptions": {
"lib": [
"es5", "es6", "dom", "dom.iterable"
]
}
}
This is required for tsconfig to know which interfaces to import while transpiling your code.
dom.iterable includes all the interfaces mentioned here : https://github.com/microsoft/TypeScript/blob/master/lib/lib.dom.iterable.d.ts
Also thanks to @domlas for mentioning this. I already upvoted his comment so that it is treated as default reason.
Solution 2 - Typescript
As I commented above, the spread operator is unfortunately currently unsupported for asynchronous iterators. The relevant issue in GitHub is tc39/proposal-async-iteration#103.
Recap from that issue (minus extraneous stuff such as "you could do it this way, oops no you can't, never mind"):
@jedwards1211 said: > Will array spread operator support ever be part of this proposal?
@domenic ansered: > Not part of this proposal... Another proposal could certainly contemplate something new here.
And I don't see a proposal elsewhere (want to start one?). In any case, since it isn't part of even JavaScript ESNext, it most likely won't get added to TypeScript.
The most viable alternative is the for await
syntax detailed in @estus's answer. Sorry I couldn't be more helpful. Good luck!
Solution 3 - Typescript
Despite what the syntax may suggest, async generator function isn't async
function that returns a generator.
As the proposal states,
> Async generator functions are similar to generator functions, with the following differences: > > When called, async generator functions return an object, an async generator whose methods (next, throw, and return) return promises for { value, done }, instead of directly returning { value, done }. This automatically makes the returned async generator objects async iterators.
It returns asynchronous generator, not a promise, so await
ing it doesn't make any good.
Since asynchronous generator has Symbol.asyncIterator
instead of Symbol.iterator
, it's non-iterable iterator, i.e. it cannot be iterated with regular ES6 methods (Array.from
, for..of
, spread syntax, etc). This is the reason why for await..of
was introduced.
The code above should be:
const values = [];
for await (const value of this.test()) {
values.push(v);
}
The iteration over asynchronous iterator can be desugared similarly to regular iterators, the difference is that next()
returns a promise of next value, not a value itself:
const iterator = this.test();
let next;
while ((next = await iterator.next()).done === false) {
values.push(next.value);
}
Since asynchronous generator spec is a proposal, the support for async iterators in ES6 iteration methods may be subject to change.
Solution 4 - Typescript
I had the same error trying to use the spread syntax (...) in a reducer(NgRx), you can see the following:
const _ingresoEgresoReducer = createReducer(initialState,
on(setItems, (state, { items }) => ({ ...state, items: [...items]})),
on(unSetItems, state => ({ ...state, items: []})),
);
It shows the error "Type 'IngresoEgreso' must have a 'Symbol.iterator' method that returns an iterator." with the part [...items]
To solve the issue I should change to
const _ingresoEgresoReducer = createReducer(initialState,
on(setItems, (state, { items }) => ({ ...state, items: [...[items]]})),
on(unSetItems, state => ({ ...state, items: []})),
);
And it works fine to me.
This solution was found at https://bobbyhadz.com/blog/typescript-type-object-must-have-symbol-iterator-method
Solution 5 - Typescript
Error went if added
"strict": false
in tsconfig.json