3

I have a Vector class that implements a Tuple, like this (simplified code, the Tuple interface is basically just an array without methods and a fixed size N):

interface Tuple<T, N extends number> extends ArrayLike<T>, Iterable<T> {
    0: T
    length: N
}

export class Vector<N extends number> implements Tuple<number, N> {
    //Implementation
}

Now I want to make my Vector iterable. When I create a variable of type Tuple, it is already iterable, but the Vector obviously isn't:

let t: Tuple<number,2> = [1,2]
for (const iterator of t) {...} //works

let v = new Vector([1,2])
for (const iterator of v) {....} //Type 'Vector<2>' must have a '[Symbol.iterator]()' method that returns an iterator.

My question is: How do I implement the "default" iterator into my vector, so I don't have to write the functions again? Can I somehow copy the Tuples or Arrays iterator? If this isn't possible, whats the best way to define my iterator? For now, I've added this (generator) method to my Vector class:

*[Symbol.iterator](): Iterator<number, any, undefined> {
       for (let i = 0; i < this.length; i++) {
           yield this[i]
       }
 }

It works, but I'm not 100% sure what it's actually doing, what the return value Iterator<number, any, undefined> specifies, and wether theres a better way to do it.

1 Answer 1

4

If you want your class to be iterable, you definitely have to provide an iterator somehow. But if you simply want to forward iteration to a property of your class, then I think you just need to implement a getter that gets the iterator from whatever you want to iterate.

For example, something like:

export class Vector<N extends number> implements Tuple<number, N> {
  constructor(public values: Tuple<number, N>) {}

  get [Symbol.iterator]() {
    return this.values[Symbol.iterator]
  }
}

And now when you iterate it, you actually get the iterator from the internal property values, to iterate over.

Playground


Is there a way to this without keeping 'values' as an object property (e.g. by assigning the Symbol.iterator directly in the constructor)?

Sure. By default the iterator property is readonly, but if you declare the iterator type in the class then you are saying it's writable. Then assign to the iterator property in the constructor.

class Vector {
  [Symbol.iterator]: () => Iterator<number>
  
  constructor(public values: number[]) {
    this[Symbol.iterator] = this.values[Symbol.iterator]
  }
}

See playground

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks that works great! Is there a way to this without keeping 'values' as an object property (e.g. by assigning the Symbol.iterator directly in the constructor)?
@Devoev Sure, why not? I've updated my answer.
Is there a way to specify the type of the Iterator, such that receivers know that the Iterator produces a tuple?
@JonasRøssum That's a good question that deserves to be a new question post :)
Yeah, I guess it is difficult to fit this in the comments. I will create a new question and link it here. EDIT: Posted separate question here: stackoverflow.com/questions/69184150/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.