Skip to content
Prev Previous commit
Next Next commit
add types for iterator helpers
  • Loading branch information
bakkot committed Apr 18, 2024
commit c4a8a08e5b96365f63dcca1a576ddc9292cabe7b
1 change: 1 addition & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ const libEntries: [string, string][] = [
["esnext.object", "lib.esnext.object.d.ts"],
["esnext.array", "lib.esnext.array.d.ts"],
["esnext.regexp", "lib.esnext.regexp.d.ts"],
["esnext.iterator", "lib.esnext.iterator.d.ts"],
["decorators", "lib.decorators.d.ts"],
["decorators.legacy", "lib.decorators.legacy.d.ts"],
];
Expand Down
1 change: 1 addition & 0 deletions src/lib/esnext.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/// <reference lib="esnext.collection" />
/// <reference lib="esnext.array" />
/// <reference lib="esnext.regexp" />
/// <reference lib="esnext.iterator" />
125 changes: 125 additions & 0 deletions src/lib/esnext.iterator.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/// <reference lib="es2015.iterable" />
export {};

// Abstract type that allows us to mark `next` as `abstract`
declare abstract class Iterator<T> {
abstract next(value?: undefined): IteratorResult<T, void>;
}

// Merge all members of `BuiltinIterator<T>` into `Iterator<T>`
interface Iterator<T> extends globalThis.BuiltinIterator<T, void, undefined> {}
Comment thread
rbuckton marked this conversation as resolved.
Outdated

// Capture the `Iterator` constructor in a type we can use in the `extends` clause of `IteratorConstructor`.
type BuiltinIteratorConstructor = typeof Iterator;

declare global {
// Global `BuiltinIterator<T>` interface that can be augmented by polyfills
interface BuiltinIterator<T, TReturn, TNext> {
/**
* Returns this iterator.
*/
[Symbol.iterator](): BuiltinIterator<T, TReturn, TNext>;

/**
* Creates an iterator whose values are the result of applying the callback to the values from this iterator.
* @param callbackfn A function that accepts up to two arguments to be used to transform values from the underlying iterator.
*/
map<U>(callbackfn: (value: T, index: number) => U): BuiltinIterator<U>;

/**
* Creates an iterator whose values are those from this iterator for which the provided predicate returns true.
* @param predicate A function that accepts up to two arguments to be used to test values from the underlying iterator.
*/
filter<S extends T>(predicate: (value: T, index: number) => value is S): BuiltinIterator<S>;

/**
* Creates an iterator whose values are those from this iterator for which the provided predicate returns true.
* @param predicate A function that accepts up to two arguments to be used to test values from the underlying iterator.
*/
filter(predicate: (value: T, index: number) => unknown): BuiltinIterator<T>;

/**
* Creates an iterator whose values are the values from this iterator, stopping once the provided limit is reached.
* @param limit The maximum number of values to yield.
*/
take(limit: number): BuiltinIterator<T>;

/**
* Creates an iterator whose values are the values from this iterator after skipping the provided count.
* @param count The number of values to drop.
*/
drop(count: number): BuiltinIterator<T>;

/**
* Creates an iterator whose values are the result of applying the callback to the values from this iterator and then flattening the resulting iterators or iterables.
* @param callback A function that accepts up to two arguments to be used to transform values from the underlying iterator into new iterators or iterables to be flattened into the result.
*/
flatMap<U>(callback: (value: T, index: number) => Iterator<U> | Iterable<U>): BuiltinIterator<U>;
Comment thread
rbuckton marked this conversation as resolved.
Outdated

/**
* Calls the specified callback function for all the elements in this iterator. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to three arguments. The reduce method calls the callbackfn function one time for each element in the iterator.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of a value from the iterator.
*/
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number) => T, initialValue: T): T;

/**
* Calls the specified callback function for all the elements in this iterator. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to three arguments. The reduce method calls the callbackfn function one time for each element in the iterator.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of a value from the iterator.
*/
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number) => U, initialValue: U): U;

/**
* Creates a new array from the values yielded by this iterator.
*/
toArray(): Array<T>;

/**
* Performs the specified action for each element in the iterator.
* @param callbackfn A function that accepts up to two arguments. forEach calls the callbackfn function one time for each element in the iterator.
*/
forEach(callbackfn: (value: T, index: number) => void): void;

/**
* Determines whether the specified callback function returns true for any element of this iterator.
* @param predicate A function that accepts up to two arguments. The some method calls
* the predicate function for each element in this iterator until the predicate returns a value
* true, or until the end of the iterator.
*/
some(predicate: (value: T, index: number) => unknown): boolean;

/**
* Determines whether all the members of this iterator satisfy the specified test.
* @param predicate A function that accepts up to two arguments. The every method calls
* the predicate function for each element in this iterator until the predicate returns
* false, or until the end of this iterator.
*/
every(predicate: (value: T, index: number) => unknown): boolean;

/**
* Returns the value of the first element in this iterator where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of this iterator, in
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
*/
find<S extends T>(predicate: (value: T, index: number) => value is S): S | undefined;
find(predicate: (value: T, index: number) => unknown): T | undefined;

readonly [Symbol.toStringTag]: string;
}

// Global `IteratorConstructor` interface that can be augmented by polyfills
interface IteratorConstructor extends BuiltinIteratorConstructor {
/**
* Creates a native iterator from an iterator or iterable object.
* Returns its input if the input already inherits from the built-in Iterator class.
* @param value An iterator or iterable object to convert a native iterator.
*/
from<T>(value: Iterator<T> | Iterable<T>): BuiltinIterator<T>;
Comment thread
rbuckton marked this conversation as resolved.
Outdated
}

var Iterator: IteratorConstructor;
}
1 change: 1 addition & 0 deletions src/lib/libs.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"esnext.collection",
"esnext.array",
"esnext.regexp",
"esnext.iterator",
"decorators",
"decorators.legacy",
// Default libraries
Expand Down
69 changes: 69 additions & 0 deletions tests/cases/compiler/builtinIterator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// @target: esnext

const iterator = Iterator.from([0, 1, 2]);

const mapped = iterator.map(String);

const filtered = iterator.filter(x => x > 0);

function isZero(x: number): x is 0 {
return x === 0;
}
const zero = iterator.filter(isZero);

const iteratorFromBare = Iterator.from({
next() {
return {
done: Math.random() < .5,
value: "a string",
};
},
});


function* gen() {
yield 0;
}

const mappedGen = gen().map(x => x === 0 ? "zero" : "other");

const mappedValues = [0, 1, 2].values().map(x => x === 0 ? "zero" : "other");


class GoodIterator extends Iterator<number> {
next() {
return { done: false, value: 0 } as const;
}
}

// error cases
new Iterator<number>();

class C extends Iterator<number> {}

// it's unfortunate that these are an error
class BadIterator1 extends Iterator<number> {
next() {
if (Math.random() < .5) {
return { done: false, value: 0 } as const;
} else {
return { done: true, value: "a string" } as const;
}
}
}

class BadIterator2 extends Iterator<number> {
next() {
return { done: false, value: 0 };
}
}

class BadIterator3 extends Iterator<number> {
next() {
if (Math.random() < .5) {
return { done: false, value: 0 };
} else {
return { done: true, value: "a string" };
}
}
}