1

I have read about and successfully added a member to the Array interface using code like this:

interface Array<T> {
    first(): T | undefined;
}

Array.prototype.first = function<T>(this: T[]): T | undefined {
    return this.length > 0 ? this[0] : undefined;
};

However, if I try to do the same to the Iterable interface, it does not work:

interface Iterable<T> {
    first(): T | undefined;
}

Iterable.prototype.first = function <T>(this: Iterable<T>): T | undefined {
    return undefined;
}

The TS compiler returns an error about the line where I am trying to modify the Iterable prototype:

'Iterable' only refers to a type, but is being used as a value here.

Is it possible to add methods to Iterable? Thanks!

Auth Infant
  • 1,760
  • 1
  • 16
  • 34
  • 1
    `Array.prototype` is the `prototype` property on the `Array` constructor, which exists at runtime, and it has the type `Array`. Types and values are [not the same](https://stackoverflow.com/a/50396312/2887218), so the fact that there is a type named `Foo` does not mean there is a value named `Foo`. Specifically, there is no `Iterable` constructor at runtime which you use to make instances of `Iterable`. So there's no `Iterable.prototype` either. – jcalz Dec 11 '18 at 00:56
  • 1
    If you want to add `first` to `Iterable` at runtime, you'll need to add it to every `Iterable` instance or the `prototype` of every constructor which produces `Iterable` objects, etc. This seems impossible to me, since `Iterable` is just a [protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol), and you won't be able to anticipate everything which implements it. Maybe someone else has a clever idea? But I'd say just keep `first` as a standalone function that takes an `Iterable` parameter. – jcalz Dec 11 '18 at 00:59

1 Answers1

1

Iterable is an interface. All it means is "this thing that implements this interface is guaranteed to have the things the interface says it does". But an interface itself is not a thing.

If you modify an interface, the only thing it will do is show you in your code, at compile time, where things that implement the interface are not doing it correctly.

Here's some things you could do, just to put you in the mind set: Declare a new interface, and make all of you code conform to it. Declare a new class and give it all the behavior you need. Or Create a static helper class that works by passing it instances of existing Iterables.

interface Iterable {
  propA: string;
  propB: string;
}

interface MyIterable<T> extends Iterable {
  propC: string;
  first: T;
  forEach: () => void;
}

class MyClassIterable<T> implements Iterable<T> {
  propA: string;
  propB: string;

  [Symbol.iterator] (): Iterator<T> {
    //
  }

}

class MyClassForMyIterable<T> implements MyIterable<T> {
  propC: string;
  first: T;
  propA: string;
  propB: string;

  [Symbol.iterator] (): Iterator<T> {
    //
  }

  forEach (): void {
    //
  }

  get first(): T | null {
    //
  }

}

abstract class IterationHelper {
  static first<T> (iterable: Iterable): T {
    return (some value from iterable)
  }
}
Cooper Buckingham
  • 2,503
  • 2
  • 15
  • 23