Issue
I'm experimenting with type script and trying to teach myself how to write generic containers as a learning experience.
I've created a LinkedList class that I want to add the ability to iterate over, example
for (const item of ll) {
}
where item
should be of type T
.
however, this is my implementation in the class of Symbol.iterator
[Symbol.iterator]() {
const head = this._head;
let node = this._head._next;
return {
next() {
if (node === head) {
return { value: undefined, done: true };
}
const r = { value: node.item, done: false };
node = node._next;
return r;
}
};
};
the problem is, that typescript then seems to interpret the type of "item" in the for loop above as T | undefined
(even though, the "value" associated with the done = true which is never seen by the consumers of the iterator.
Wondering what the appropriate way to handle this is, as this is all new to me, and googling around hasn't helped me find the answer to this Q.
The closest I found to answering my Q is to use a generator instead.
i.e.
*[Symbol.iterator]() {
const head = this._head;
let node = this._head._next;
while (node !== head) {
yield node.item;
node = node._next;
}
};
since it never yields anything besides a T
, the typing works better.
But, this sees "wrong" to me that the first doesn't work as I expect, but the second does (even though I can see why in practice it works that way, next()
, returns 2 possible types for value
, while the generator method only ever ever returns 1 type). Does one have to use a generator to get what I want, or should a non generator iterator work as well?
In looking closely at the transpiled javascript code, typescript seems to go "wild" (to me, I'm new at this) with the generator version (creating what seems to be a very complicated _generator
object), where the initial non generator version is basically the exact code I wrote.
As an aside, I realize that if nodes can be removed from my linked list, that the iterator could become problematic (ala a ConcurrentModificationException error in java).
Solution
Your method [Symbol.iterator]()
is returning two possibilites, it's either returning { value: undefined, done: boolean }
or { value: T, done: boolean}
and Typescript will try it's best to make a type based on your return values.
The "for...of" loop will use the types from value
and in this case it's either T
or undefined
.
Instead of letting Typescript guess the return value of your iterator you can tell what it's returning with the generic type Iterator<>
[Symbol.iterator](): Iterator<T> {
// implementation
}
I think you can make a simplier implementation of your Iterator object if you combine Iterator and the Iterable protocol and make a IterableIterator
class MyIterator<T> implements IterableIterator<T> {
next(): IteratorResult<T> {
// next implementation
}
[Symbol.iterator]() {
return this
}
}
I have written an article about Iterator, Iterable, IterableIterator and Generators that explains this more in depth at https://medium.com/@lostfields if you are interested.
Answered By - Lostfields
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.