Skip to content

Instantly share code, notes, and snippets.

@Orangetronic
Created May 12, 2025 11:11
Show Gist options
  • Save Orangetronic/c757117f13dde122796247042c2237f3 to your computer and use it in GitHub Desktop.
Save Orangetronic/c757117f13dde122796247042c2237f3 to your computer and use it in GitHub Desktop.
Iterable Weak Set in TypeScript

Iterable WeakSet

We have WeakSet in JS now, but sometimes i've run into situations where I want to be able to iterate over a WeakSet's contents.

We can accomplish this using a regular Set holding WeakRefs, and a WeakMap to tie items themselves to their WeakRefs.

Implementation notes

This only cleans up empty WeakRefs when it is iterated, so will retain empty WeakRef items unless you iterate it sometimes.

Further work

The following Set methods & properties are not implemented:

size
clear()
difference()
entries()
forEach()
intersection()
isDisjointFrom()
isSubsetOf()
isSupersetOf()
keys()
symmetricDifference()
union()
values()
/**
* It's WeakSet, but Iterable!
*
* This uses WeakRefs internally. These get cleaned up whenever it is iterated,
* or whenever an entry is deleted.
*
* If you instantiate one of these and expect it to live forever, but do not iterate
* it often, maybe do that on a very slow interval to clean up empty WeakRefs.
*/
export class IterableWeakSet<T extends object> {
private _internal_set: Set<WeakRef<T>>;
private _key_map: WeakMap<T, WeakRef<T>>
constructor(entries?: Array<T>) {
this._internal_set = new Set()
this._key_map = new WeakMap()
if (entries) {
entries.forEach(entry => this.add(entry))
}
}
add(entry: T) {
if (this.has(entry)) return this
const ref = new WeakRef(entry)
this._internal_set.add(ref)
this._key_map.set(entry, ref)
return this
}
delete(entry: T) {
const ref = this._key_map.get(entry)
if (!ref) return false
this._internal_set.delete(ref)
this._key_map.delete(entry)
return true
}
has(entry: T) {
const ref = this._key_map.get(entry)
const internalentry = ref?.deref()
return !!internalentry
}
*[Symbol.iterator]() {
for (const ref of this._internal_set) {
const entry = ref.deref()
// if the contents of a ref have been garbage collected, clean up its entry
// from the internal set and continue
if (!entry) {
this._internal_set.delete(ref)
continue
} else {
yield entry
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment