Hi,
Getting a compile error when trying to compile:
// ul is an HTMLElement
for (let child of ul.children) {
//body
}
I get the following error: error TS2495: Type 'HTMLCollection' is not an array type or a string type.
Is there a way to get type support for this? Or is too much work to bother with?
HTMLCollection is not an array.
https://developer.mozilla.org/en-US/docs/Web/API/NodeList#Why_can't_I_use_forEach_or_map_on_a_NodeList.3F
xLama, I am aware. However, the compiled code only needs an interface of .length, which means psuedoarrays should also be able to compile with the for..of syntax, right?
I think If ECMAScript does not support HTMLCollection like an array, TypeScript neither.
However ECMAScript 6 supports it, so I am agree.
That's a good point!
You can do this por the moment:
var foo:HTMLCollection;
var bar = [].slice.call(foo);
for (var g of bar){}
Yeah. The other thing you can do (if you promise not to mess with the body of the DOM in the loop) is:
for(let c of <any>bar) {}
Thanks! :)
ES6 does not support HTMLCollection on for-of loop as it is not iterable. NodeList is iterable and should be supported. Check #2695.
@SaschaNaz What about this? #2696
for-of only works on arrays for ES3/ES5 by design.
@xLama You are right. A limited number of iterable interfaces should be supported but currently not. #4947 is also related.
@SaschaNaz There is a problem. How do you want to iterate them when emit to ES3/5 without complicate it?
i think this is something that's missing from lib.dom.iterable.d.ts
i haven't tested other browsers but chrome and firefox both return a function (named values) for HTMLCollection.prototype[Symbol.iterator]
not that i know which one i should be reading but mdn provides some spec links for HTMLCollection
or is this something wrong with the spec? in "living standard" and "dom4" i can see NodeList has iterable<Node>; but HTMLCollection doesn't seem to have anything like that (is wanting to conveniently loop through an element's children really that uncommon?)
const myElement: Element = document.body;
const children: HTMLCollection = myElement.children;
// is this what i'm supposed to write?
for (const child of <IterableIterator<Element>> Array.prototype[Symbol.iterator].call(children)) {
console.log(child);
}
// it's somehow even longer than the traditional ugly method
for (let i = 0; i < children.length; i++) { const child = children[i];
console.log(child);
}
// this method is cheating but it also works:
interface HTMLCollection {
[Symbol.iterator](): IterableIterator<Element>;
}
for (const child of children) {
console.log(child);
}
Looking at this again, i believe the TS definitions do match the spec. NodeList is iterable, but HTMLCollection is not. and HTMLElement.children is an HTMLCollection see https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children.
If the spec is wrong, then we need to change it, but i do not think i have enough DOM expertise to say which is correct.
If the interface has any of the following:
- an iterable declaration
- an indexed property getter and an integer-typed attribute named “length”
- a maplike declaration
- a setlike declaration
then a property must exist whose name is the
@@iteratorsymbol, with attributes{ [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }and whose value is a function object.
Chromium on April 2016 implemented and merged the change based on this criteria.
This means we can add [Symbol.iterator] key, and also means that TSJS-lib-generator can automatically add the key but without methods e.g. values().
Microsoft/TSJS-lib-generator#235 to fix this.
I see that PR was merged in May, but I still have this error with latest TS (2.4.2)
@felixfbecker have you tried turning on --downlevelIteration?
No, didn't know that existed, but as I understand it I don't need it with target: ES6. Here's the error:
[ts] Type must have a '[Symbol.iterator]()' method that returns an iterator
when I jump to definition the typings don't have the Symbol.iterator defined. The flag doesn't help :/
The PR https://github.com/Microsoft/TSJS-lib-generator/pull/235 added dom.es6.generated.d.ts but TS hasn't merged the file into the build process yet.
Is there a reason why not the MDN's example of for-of iterator is not working? I have a slight memory that it was working before:
var iterable = {
[Symbol.iterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return { value: this.i++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (var value of iterable) {
console.log(value); // Error: is not an array type or a string type.
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
Here is the code in playground:
http://www.typescriptlang.org/play/index.html#src=var%20iterable%20%3D%20%7B%0A%20%20%5BSymbol.iterator%5D()%20%7B%0A%20%20%20%20return%20%7B%0A%20%20%20%20%20%20i%3A%200%2C%0A%20%20%20%20%20%20next()%20%7B%0A%20%20%20%20%20%20%20%20if%20(this.i%20%3C%203)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%20%7B%20value%3A%20this.i%2B%2B%2C%20done%3A%20false%20%7D%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%7B%20value%3A%20undefined%2C%20done%3A%20true%20%7D%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%7D%3B%0A%0Afor%20(var%20value%20of%20iterable)%20%7B%0A%20%20console.log(value)%3B%0A%7D
You need to compile with --downlevelIteration when targeting ES3/ES5, which is not supported in the playground at the moment.
Worked like a charm, thanks!
Most helpful comment
Yeah. The other thing you can do (if you promise not to mess with the body of the DOM in the loop) is: