function* iterateSetLike(setLike: ReadonlySetLike): IterableIterator { const iterator = setLike.keys() for (let next = iterator.next(); !next.done; next = iterator.next()) { yield next.value } } export class SetOverlay extends Set { public deleted: Set = new Set() public source: Set private appendedSource: Set = new Set() private sourceCleared = false public constructor(source: Set) { super() this.source = source } public add(value: T): this { const shouldAppendSourceValue = this.appendedSource.has(value) || (this.sourceCleared && this.deleted.has(value)) if (this.source.has(value) && shouldAppendSourceValue) { this.deleted.delete(value) this.appendedSource.add(value) return super.add(value) } if (this.source.has(value)) { this.deleted.delete(value) this.appendedSource.delete(value) return this } return super.add(value) } public hasOwn(member: T): boolean { return super.has(member) } public has(key: T): boolean { return super.has(key) || (!this.deleted.has(key) && this.source.has(key)) } public delete(key: T): boolean { if (this.source.has(key)) { this.deleted.add(key) this.appendedSource.delete(key) super.delete(key) return true } return super.delete(key) } public clear(): void { this.deleted = new Set(this.source) this.appendedSource.clear() this.sourceCleared = true super.clear() } public *[Symbol.iterator](): SetIterator { for (const value of this.source) { if (!this.deleted.has(value) && !this.appendedSource.has(value)) { yield value } } yield* super[Symbol.iterator]() } public *keys(): SetIterator { yield* this[Symbol.iterator]() } public *values(): SetIterator { yield* this[Symbol.iterator]() } public *entries(): SetIterator<[T, T]> { for (const value of this[Symbol.iterator]()) { yield [value, value] } } public forEach( callbackfn: (value: T, value2: T, set: Set) => void, thisArg?: any, ): void { for (const value of this[Symbol.iterator]()) { callbackfn.call(thisArg, value, value, this) } } public union(other: ReadonlySetLike): Set { const result = new Set(this) for (const value of iterateSetLike(other)) { result.add(value) } return result } public intersection(other: ReadonlySetLike): Set { const result = new Set() const otherSet = other as ReadonlySetLike const thisSet = this as ReadonlySetLike if (this.size <= other.size) { for (const value of this) { if (otherSet.has(value)) { result.add(value as T & U) } } } else { for (const value of iterateSetLike(other)) { if (thisSet.has(value)) { result.add(value as T & U) } } } return result } public difference(other: ReadonlySetLike): Set { const result = new Set() const otherSet = other as ReadonlySetLike if (this.size <= other.size) { for (const value of this) { if (!otherSet.has(value)) { result.add(value) } } } else { for (const value of this) { result.add(value) } for (const value of iterateSetLike(other)) { result.delete(value as unknown as T) } } return result } public symmetricDifference(other: ReadonlySetLike): Set { const result = new Set(this) for (const value of iterateSetLike(other)) { if (result.has(value)) { result.delete(value) } else { result.add(value) } } return result } public isSubsetOf(other: ReadonlySetLike): boolean { if (this.size > other.size) { return false } for (const value of this) { if (!other.has(value)) { return false } } return true } public isSupersetOf(other: ReadonlySetLike): boolean { if (this.size < other.size) { return false } for (const value of iterateSetLike(other)) { if (!this.has(value as T)) { return false } } return true } public isDisjointFrom(other: ReadonlySetLike): boolean { if (this.size <= other.size) { for (const value of this) { if (other.has(value)) { return false } } } else { for (const value of iterateSetLike(other)) { if (this.has(value as T)) { return false } } } return true } public *iterateOwn(): SetIterator { yield* super[Symbol.iterator]() } public get size(): number { return ( super.size + this.source.size - this.deleted.size - this.appendedSource.size ) } }