diff --git a/__tests__/map-set.js b/__tests__/map-set.js index a8e0db39..52bd911e 100644 --- a/__tests__/map-set.js +++ b/__tests__/map-set.js @@ -331,6 +331,36 @@ function runBaseTest(name, autoFreeze, useListener) { expect(Array.from(newSet)).toEqual([items[0], items[1]]) }) + + it("IterableIterator should return itself", () => { + const baseState = { + map: new Map([1, 2, 3].map(i => [i, `Item ${i}`])) + } + + produce(baseState, draft => { + const it = draft.map.values() + expect(it.next()).toEqual({value: "Item 1", done: false}) + + const itOfIt = it[Symbol.iterator]() + expect(itOfIt.next()).toEqual({value: "Item 2", done: false}) + expect(itOfIt).toBe(it) + + const it2 = draft.map.values() + expect(it2.next()).toEqual({value: "Item 1", done: false}) + }) + }) + + it("is compatible with ES2025 features", () => { + const baseState = { + map: new Map([1, 2, 3].map(i => [i, `Item ${i}`])) + } + + const result = produce(baseState, draft => { + draft.map = new Map(draft.map.entries().filter(([k]) => k % 2 === 1)) + }) + + expect(result.map.keys().toArray()).toEqual([1, 3]) + }) }) describe("set issues " + name, () => { test("#819.A - maintains order when adding", () => { diff --git a/src/plugins/mapset.ts b/src/plugins/mapset.ts index 3c59eab0..7fd23e92 100644 --- a/src/plugins/mapset.ts +++ b/src/plugins/mapset.ts @@ -20,6 +20,15 @@ import { handleCrossReference } from "../internal" +declare global { + // `Iterator.from` was added in ES2025. + var Iterator: + | undefined + | { + from(iterable: Iterator): IterableIterator + } +} + export function enableMapSet() { class DraftMap extends Map { [DRAFT_STATE]: MapState @@ -126,8 +135,7 @@ export function enableMapSet() { values(): IterableIterator { const iterator = this.keys() - return { - [Symbol.iterator]: () => this.values(), + return iteratorFrom({ next: () => { const r = iterator.next() /* istanbul ignore next */ @@ -138,13 +146,12 @@ export function enableMapSet() { value } } - } as any + }) } entries(): IterableIterator<[any, any]> { const iterator = this.keys() - return { - [Symbol.iterator]: () => this.entries(), + return iteratorFrom({ next: () => { const r = iterator.next() /* istanbul ignore next */ @@ -155,7 +162,7 @@ export function enableMapSet() { value: [r.value, value] } } - } as any + }) } [Symbol.iterator]() { @@ -163,6 +170,21 @@ export function enableMapSet() { } } + function iteratorFrom( + iterable: Iterator + ): IterableIterator { + if (typeof Iterator !== "undefined") { + return Iterator.from(iterable) + } + + const iterator: IterableIterator = { + ...iterable, + [Symbol.iterator]: () => iterator + } + + return iterator + } + function proxyMap_( target: T, parent?: ImmerState