Home > Enterprise >  stating implements Iterable<T> is redundant
stating implements Iterable<T> is redundant

Time:02-04

My class wraps an iterable object and implements iteration as:

[Symbol.iterator]() { return this.range[Symbol.iterator](); }

The compiler does not enforce the implements Iterable<T> info - why is that?

class SomeWrapper /* implements Iterable<number> */ {
  constructor(public readonly range: MyRange) {}
  [Symbol.iterator]() { return this.range[Symbol.iterator](); }
}

class MyRange {
  constructor(
    public readonly begin: number,
    public readonly end: number
  ) {}

  [Symbol.iterator]() {
    return new MyRangeIterator(this);
  }
}

class MyRangeIterator implements Iterator<number>
{
  public index: number
  public end: number

  constructor(range: MyRange)
  {
    this.index = range.begin;
    this.end = range.end 
  }

  public next(): IteratorResult<number, "no_more_values">
  {
    if (this.index < this.end) {
      return { done: false, value: this.index   }
    }
    return { done: true, value: "no_more_values" }
  }
}

usage:

const range = new MyRange(5, 14);
const wrapper = new SomeWrapper(range)
for (const x of wrapper) { // I expected an error here: SomeWrapper - Not Iterable
  console.log(x)
}

CodePudding user response:

TypeScript doesn't check if your class implements Iterable but checks if your class has Symbol.iterator property.

It will no longer typecheck when you remove this property or make this property not to return an iterator.

Iterators and Generators explains it.

An object is deemed iterable if it has an implementation for the Symbol.iterator property.

TypeScript typechecks in this way in general as it's based on structural subtyping.

For example, this typechecks even though Y doesn't implements X.

interface X {
    readonly x: string;
}

class Y {
    readonly x: string = 'y';
}

function f(x: X): void {}

f(new Y());
  • Related