/iterator-polyfill

Provide a polyfill for iterators helpers proposal

Primary LanguageTypeScript

iterator-polyfill

A fork of alkihis/iterator-polyfill, with fixed typos and spec-complient reduce implementation

Provide a polyfill for all methods defined in iterator helpers proposal, both for Iterator and AsyncIterator.

Installation

Install it with npm/yarn/what you want.

npm i iterator-polyfill

Include it into your JavaScript/TypeScript code.

// With ECMA modules
import 'iterator-polyfill';

// or with CommonJS
require('iterator-polyfill');

TypeScript types are bundled within the package as global interfaces, so using the base iterators (array iterators, generators) should not throw type warnings.

You can also use script tag implementation for use in browser environnements:

<script src="https://unpkg.com/iterator-polyfill/dist/index.js"></script>

Implementation

The polyfill auto applies to iterators used by arrays, generators and async generators. Usually, you don't need to polyfill them.

For your own objects, you can use globalThis.Iterator.prototype and globalThis.AsyncIterator.prototype. Your JS engine must support async generator and Symbol.asyncIterator property in order to read the polyfill properly.

const my_it = {
  next() {}
  /** implement throw(), return() */
};

Object.setPrototypeOf(my_it, Iterator.prototype);

You can also extends Iterator or AsyncIterator in your own classes, and implement .next in them.

class MyIterator extends Iterator {
  next() {
    return {
      value: 3,
      done: false,
    };
  }
}

Example

Iterator and AsyncIterator are directly bundled into most of needed prototypes.

function* numbers() {
  yield 1;
  yield 2;
  yield 3;
}

async function* asyncNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

numbers()
  .map(e => e * 2)
  .take(2)
  .toArray(); // [2, 4]

asyncNumbers()
  .filter(e => !!(e % 2))
  .map(e => String(e))
  .toArray(); // Promise<["1", "3"]>

API

As the proposal purpose, there are a few methods for each prototype. They're presented here with TypeScript types.

interface Iterator<T, TReturn = any, TNext = undefined> {
  /** Map each value of iterator to another value via {callback}. */
  map<R>(callback: (value: T) => R) : Iterator<R, TReturn, TNext>;
  /** Each value is given through {callback}, return `true` if value is needed into returned iterator. */
  filter(callback: (value: T) => boolean) : Iterator<T, TReturn, TNext>;
  /** Create a new iterator that consume {limit} items, then stops. */
  take(limit: number) : Iterator<T, TReturn, TNext>;
  /** Create a new iterator that skip {limit} items from source iterator, then yield all values. */
  drop(limit: number) : Iterator<T, TReturn, TNext>;
  /** Get a pair [index, value] for each remaining value of iterable. */
  asIndexedPairs() : Iterator<[number, T], TReturn, TNext>;
  /** Like map, but you can return a new iterator that will be flattened. */
  flatMap<R>(mapper: (value: T) => Iterator<R> | R) : Iterator<R, TReturn, TNext>;
  /** Find a specific value that returns `true` in {callback}, and return it. Returns `undefined` otherwise. */
  find(callback: (value: T) => boolean) : T | undefined;
  /** Return `true` if each value of iterator validate {callback}. */
  every(callback: (value: T) => boolean) : boolean;
  /** Return `true` if one value of iterator validate {callback}. */
  some(callback: (value: T) => boolean) : boolean;
  /** Consume iterator and collapse values inside an array. */
  toArray(max_count?: number) : T[];
  /** Accumulate each item inside **acc** for each value **value**. */
  reduce<V>(reducer: (acc: V, value: T) => V, initial_value?: V) : V;
  /** Iterate over each value of iterator by calling **callback** for each value. */
  forEach(callback: (value: T) => any) : void;
}

// with
type PromiseOrType<T> = Promise<T> | T;
// then

interface AsyncIterator<T, TReturn = any, TNext = undefined> {
  /** Map each value of iterator to another value via {callback}. */
  map<R>(callback: (value: T) => PromiseOrType<R>) : AsyncIterator<R, TReturn, TNext>;
  /** Each value is given through {callback}, return `true` if value is needed into returned iterator. */
  filter(callback: (value: T) => PromiseOrType<boolean>) : AsyncIterator<T, TReturn, TNext>;
  /** Create a new iterator that consume {limit} items, then stops. */
  take(limit: number) : AsyncIterator<T, TReturn, TNext>;
  /** Create a new iterator that skip {limit} items from source iterator, then yield all values. */
  drop(limit: number) : AsyncIterator<T, TReturn, TNext>;
  /** Get a pair [index, value] for each remaining value of iterable. */
  asIndexedPairs() : AsyncIterator<[number, T], TReturn, TNext>;
  /** Like map, but you can return a new iterator that will be flattened. */
  flatMap<R>(mapper: (value: T) => AsyncIterator<R> | R) : AsyncIterator<R, TReturn, TNext>;
  /** Find a specific value that returns `true` in {callback}, and return it. Returns `undefined` otherwise. */
  find(callback: (value: T) => PromiseOrType<boolean>) : Promise<T | undefined>;
  /** Return `true` if each value of iterator validate {callback}. */
  every(callback: (value: T) => PromiseOrType<boolean>) : Promise<boolean>;
  /** Return `true` if one value of iterator validate {callback}. */
  some(callback: (value: T) => PromiseOrType<boolean>) : Promise<boolean>;
  /** Consume iterator and collapse values inside an array. */
  toArray(max_count?: number) : Promise<T[]>;
  /** Accumulate each item inside **acc** for each value **value**. */
  reduce<V>(reducer: (acc: V, value: T) => PromiseOrType<V>, initial_value?: V) : Promise<V>;
  /** Iterate over each value of iterator by calling **callback** for each value. */
  forEach(callback: (value: T) => PromiseOrType<any>) : Promise<void>;
}

A few more methods has been implemented, but they're not part of the specification. See index.ts to see them inside the Iterator and AsyncIterator interface.