-
Notifications
You must be signed in to change notification settings - Fork 13.8k
feat: add iterator methods to esnext #64095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| /*! ***************************************************************************** | ||
| Copyright (c) Microsoft Corporation. All rights reserved. | ||
| Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
| this file except in compliance with the License. You may obtain a copy of the | ||
| License at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
| WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
| MERCHANTABILITY OR NON-INFRINGEMENT. | ||
|
|
||
| See the Apache Version 2.0 License for specific language governing permissions | ||
| and limitations under the License. | ||
| ***************************************************************************** */ | ||
|
|
||
|
|
||
| /// <reference lib="es2025.iterator" /> | ||
|
|
||
| export {}; | ||
|
|
||
| interface IteratorZipShortestOptions { | ||
| /** | ||
| * Stops when any input is exhausted. | ||
| */ | ||
| mode?: "shortest"; | ||
| } | ||
|
|
||
| interface IteratorZipLongestOptions<T> { | ||
| /** | ||
| * Continues until every input is exhausted. | ||
| */ | ||
| mode: "longest"; | ||
|
|
||
| /** | ||
| * Values used when an input is exhausted before the others. | ||
| */ | ||
| padding?: T; | ||
| } | ||
|
|
||
| interface IteratorZipStrictOptions { | ||
| /** | ||
| * Requires every input to yield the same number of values. | ||
| * If not, a `TypeError` will be thrown when an input is exhausted before others. | ||
| */ | ||
| mode: "strict"; | ||
| } | ||
|
|
||
| type IteratorZipOptions<T> = | ||
| | IteratorZipShortestOptions | ||
| | IteratorZipLongestOptions<T> | ||
| | IteratorZipStrictOptions; | ||
|
|
||
| type IteratorInput<T> = Iterable<T> | Iterator<T>; | ||
|
|
||
| type IteratorInputTuple = readonly [] | readonly [IteratorInput<unknown>, ...IteratorInput<unknown>[]]; | ||
|
|
||
| type IteratorYield<T> = T extends IteratorInput<infer U> ? U : never; | ||
|
|
||
| type IteratorZipResult<T, TExtra = never> = { | ||
| -readonly [K in keyof T]: IteratorYield<T[K]> | TExtra; | ||
| }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a difference in these mapped types other than the constraint? I think you can unify them.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done - unified both types into |
||
|
|
||
| declare global { | ||
| interface IteratorObject<T, TReturn, TNext> { | ||
| /** | ||
| * Creates an iterator whose values are arrays containing successive values from this iterator. | ||
| * @param chunkSize The maximum number of values in each array. | ||
| */ | ||
| chunks(chunkSize: number): IteratorObject<T[], undefined, unknown>; | ||
|
|
||
| /** | ||
| * Creates a string by concatenating the values of this iterator, separated by the specified separator. | ||
| * `null` and `undefined` values contribute an empty string. | ||
| * @param separator A string used to separate values. If omitted, a comma is used. | ||
| */ | ||
| join(separator?: string): string; | ||
|
|
||
| /** | ||
| * Determines whether this iterator yields the specified value using SameValueZero comparison. | ||
| * @param searchElement The value to locate. | ||
| * @param skippedElements The number of values to skip before searching. | ||
| */ | ||
| includes(searchElement: T, skippedElements?: number): boolean; | ||
| } | ||
|
|
||
| interface IteratorConstructor { | ||
| /** | ||
| * Creates an iterator whose values are arrays containing values yielded at the same position by each input iterator or iterable. | ||
| * @param iterables An iterable of iterators or iterables to zip. | ||
| * @param options Controls how differing input lengths are handled. | ||
| */ | ||
| zip(iterables: readonly [], options?: IteratorZipOptions<Iterable<unknown>>): IteratorObject<never, undefined, unknown>; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a little funny that we need this overload. I don't know what we'd do instead other than a conditional.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't see any test for an empty list of iterables.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added additional tests |
||
|
|
||
| /** | ||
| * Creates an iterator whose values are arrays containing values yielded at the same position by each input iterator or iterable. | ||
| * @param iterables An iterable of iterators or iterables to zip. | ||
| * @param options Controls how differing input lengths are handled. | ||
| */ | ||
| zip<T extends IteratorInputTuple>(iterables: T, options: IteratorZipLongestOptions<NoInfer<IteratorZipResult<T>>> & { padding: NoInfer<IteratorZipResult<T>>; }): IteratorObject<IteratorZipResult<T>, undefined, unknown>; | ||
| zip<T extends readonly IteratorInput<unknown>[] | []>(iterables: T, options?: IteratorZipShortestOptions | IteratorZipStrictOptions): IteratorObject<IteratorZipResult<T>, undefined, unknown>; | ||
| zip<T extends readonly IteratorInput<unknown>[] | []>(iterables: T, options: IteratorZipOptions<NoInfer<Partial<IteratorZipResult<T>>>>): IteratorObject<IteratorZipResult<T, undefined>, undefined, unknown>; | ||
|
|
||
| /** | ||
| * Creates an iterator whose values are arrays containing values yielded at the same position by each input iterator or iterable. | ||
| * @param iterables An iterable of iterators or iterables to zip. | ||
| * @param options Controls how differing input lengths are handled. | ||
| */ | ||
| zip<T extends IteratorInput<unknown>>(iterables: Iterable<T>, options?: IteratorZipShortestOptions | IteratorZipStrictOptions): IteratorObject<IteratorYield<T>[], undefined, unknown>; | ||
| zip<T extends IteratorInput<unknown>>(iterables: Iterable<T>, options: IteratorZipOptions<NoInfer<Iterable<IteratorYield<T>>>>): IteratorObject<(IteratorYield<T> | undefined)[], undefined, unknown>; | ||
|
|
||
| /** | ||
| * Creates an iterator whose values are objects containing values yielded at the same position by each iterator or iterable in the input object. | ||
| * @param iterables An object whose enumerable own properties contain iterators or iterables to zip. | ||
| * @param options Controls how differing input lengths are handled. | ||
| */ | ||
| zipKeyed<T extends object>(iterables: T & Record<keyof T, IteratorInput<unknown>>, options?: IteratorZipShortestOptions | (IteratorZipLongestOptions<NoInfer<IteratorZipResult<T>>> & { padding: NoInfer<IteratorZipResult<T>>; }) | IteratorZipStrictOptions): IteratorObject<IteratorZipResult<T>, undefined, unknown>; | ||
| zipKeyed<T extends object>(iterables: T & Record<keyof T, IteratorInput<unknown>>, options: IteratorZipOptions<NoInfer<Partial<IteratorZipResult<T>>>>): IteratorObject<IteratorZipResult<T, undefined>, undefined, unknown>; | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| iteratorChunks.ts(3,33): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
|
|
||
|
|
||
| ==== iteratorChunks.ts (1 errors) ==== | ||
| const chunks: number[][] = Iterator.from([1, 2, 3]).chunks(2).toArray(); | ||
|
|
||
| Iterator.from([1, 2, 3]).chunks("2"); | ||
| ~~~ | ||
| !!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| //// [tests/cases/compiler/iteratorChunks.ts] //// | ||
|
|
||
| //// [iteratorChunks.ts] | ||
| const chunks: number[][] = Iterator.from([1, 2, 3]).chunks(2).toArray(); | ||
|
|
||
| Iterator.from([1, 2, 3]).chunks("2"); | ||
|
|
||
|
|
||
| //// [iteratorChunks.js] | ||
| "use strict"; | ||
| const chunks = Iterator.from([1, 2, 3]).chunks(2).toArray(); | ||
| Iterator.from([1, 2, 3]).chunks("2"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //// [tests/cases/compiler/iteratorChunks.ts] //// | ||
|
|
||
| === iteratorChunks.ts === | ||
| const chunks: number[][] = Iterator.from([1, 2, 3]).chunks(2).toArray(); | ||
| >chunks : Symbol(chunks, Decl(iteratorChunks.ts, 0, 5)) | ||
| >Iterator.from([1, 2, 3]).chunks(2).toArray : Symbol(IteratorObject.toArray, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >Iterator.from([1, 2, 3]).chunks : Symbol(IteratorObject.chunks, Decl(lib.esnext.iterator.d.ts, --, --)) | ||
| >Iterator.from : Symbol(IteratorConstructor.from, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >from : Symbol(IteratorConstructor.from, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >chunks : Symbol(IteratorObject.chunks, Decl(lib.esnext.iterator.d.ts, --, --)) | ||
| >toArray : Symbol(IteratorObject.toArray, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
|
|
||
| Iterator.from([1, 2, 3]).chunks("2"); | ||
| >Iterator.from([1, 2, 3]).chunks : Symbol(IteratorObject.chunks, Decl(lib.esnext.iterator.d.ts, --, --)) | ||
| >Iterator.from : Symbol(IteratorConstructor.from, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >from : Symbol(IteratorConstructor.from, Decl(lib.es2025.iterator.d.ts, --, --)) | ||
| >chunks : Symbol(IteratorObject.chunks, Decl(lib.esnext.iterator.d.ts, --, --)) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| //// [tests/cases/compiler/iteratorChunks.ts] //// | ||
|
|
||
| === iteratorChunks.ts === | ||
| const chunks: number[][] = Iterator.from([1, 2, 3]).chunks(2).toArray(); | ||
| >chunks : number[][] | ||
| >Iterator.from([1, 2, 3]).chunks(2).toArray() : number[][] | ||
| >Iterator.from([1, 2, 3]).chunks(2).toArray : () => number[][] | ||
| >Iterator.from([1, 2, 3]).chunks(2) : IteratorObject<number[], undefined, unknown> | ||
| >Iterator.from([1, 2, 3]).chunks : (chunkSize: number) => IteratorObject<number[], undefined, unknown> | ||
| >Iterator.from([1, 2, 3]) : IteratorObject<number, undefined, unknown> | ||
| >Iterator.from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown> | ||
| >Iterator : IteratorConstructor | ||
| >from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown> | ||
| >[1, 2, 3] : number[] | ||
| >1 : 1 | ||
| >2 : 2 | ||
| >3 : 3 | ||
| >chunks : (chunkSize: number) => IteratorObject<number[], undefined, unknown> | ||
| >2 : 2 | ||
| >toArray : () => number[][] | ||
|
|
||
| Iterator.from([1, 2, 3]).chunks("2"); | ||
| >Iterator.from([1, 2, 3]).chunks("2") : IteratorObject<number[], undefined, unknown> | ||
| >Iterator.from([1, 2, 3]).chunks : (chunkSize: number) => IteratorObject<number[], undefined, unknown> | ||
| >Iterator.from([1, 2, 3]) : IteratorObject<number, undefined, unknown> | ||
| >Iterator.from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown> | ||
| >Iterator : IteratorConstructor | ||
| >from : <T>(value: Iterable<T, unknown, undefined> | Iterator<T, unknown, undefined>) => IteratorObject<T, undefined, unknown> | ||
| >[1, 2, 3] : number[] | ||
| >1 : 1 | ||
| >2 : 2 | ||
| >3 : 3 | ||
| >chunks : (chunkSize: number) => IteratorObject<number[], undefined, unknown> | ||
| >"2" : "2" | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| iteratorIncludes.ts(4,35): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
| iteratorIncludes.ts(6,38): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
|
|
||
|
|
||
| ==== iteratorIncludes.ts (2 errors) ==== | ||
| const includes: boolean = Iterator.from([1, 2, 3]).includes(2); | ||
| const includesAfterSkipping: boolean = Iterator.from([1, 2, 3]).includes(2, 1); | ||
|
|
||
| Iterator.from([1, 2, 3]).includes("1"); | ||
| ~~~ | ||
| !!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
|
|
||
| Iterator.from([1, 2, 3]).includes(2, "1"); | ||
| ~~~ | ||
| !!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| //// [tests/cases/compiler/iteratorIncludes.ts] //// | ||
|
|
||
| //// [iteratorIncludes.ts] | ||
| const includes: boolean = Iterator.from([1, 2, 3]).includes(2); | ||
| const includesAfterSkipping: boolean = Iterator.from([1, 2, 3]).includes(2, 1); | ||
|
|
||
| Iterator.from([1, 2, 3]).includes("1"); | ||
|
|
||
| Iterator.from([1, 2, 3]).includes(2, "1"); | ||
|
|
||
|
|
||
| //// [iteratorIncludes.js] | ||
| "use strict"; | ||
| const includes = Iterator.from([1, 2, 3]).includes(2); | ||
| const includesAfterSkipping = Iterator.from([1, 2, 3]).includes(2, 1); | ||
| Iterator.from([1, 2, 3]).includes("1"); | ||
| Iterator.from([1, 2, 3]).includes(2, "1"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.