@rbxts/jsnatives
WeakMap and WeakSet with Symbol Support
Enhanced versions of WeakMap and WeakSet that support using symbols as keys.
Signature
interface WeakMapWithSymbol<K extends object | symbol, V> extends Map<K, V> {}
interface WeakMapConstructor { new<K extends object | symbol, V>(): WeakMapWithSymbol<K, V> new<K extends object | symbol, V>(entries: ReadonlyArray<[K, V]>): WeakMapWithSymbol<K, V>}
interface WeakSetWithSymbol<T extends object | symbol> extends Set<T> {}
interface WeakSetConstructor { new<T extends object | symbol>(): WeakSetWithSymbol<T> new<T extends object | symbol>(values: ReadonlyArray<T>): WeakSetWithSymbol<T>}
Description
The Symbol module enhances the standard WeakMap and WeakSet collections to support symbols as keys, in addition to objects. This extension allows for memory-efficient tracking of symbols without preventing them from being garbage collected when they are no longer referenced elsewhere in the code.
These enhanced collections maintain the same API as their standard counterparts but with expanded type definitions to accept symbols.
Examples
WeakMap with Symbol Keys
// Create a WeakMap that can use symbols as keysconst metadataMap = new WeakMap<symbol | object, any>();
// Store metadata for symbolsconst entitySymbol = Symbol("entity");metadataMap.set(entitySymbol, { type: "player", createdAt: Date.now()});
// Check if a symbol has metadataprint(metadataMap.has(entitySymbol)); // true
// Retrieve the metadataconst metadata = metadataMap.get(entitySymbol);print(metadata.type); // "player"
// Delete the metadatametadataMap.delete(entitySymbol);print(metadataMap.has(entitySymbol)); // false
WeakSet with Symbol Values
// Create a WeakSet that can store symbolsconst registeredSymbols = new WeakSet<symbol | object>();
// Register some symbolsconst sym1 = Symbol("one");const sym2 = Symbol("two");
registeredSymbols.add(sym1);registeredSymbols.add(sym2);
// Check if a symbol is registeredprint(registeredSymbols.has(sym1)); // trueprint(registeredSymbols.has(Symbol("not registered"))); // false
// Remove a symbolregisteredSymbols.delete(sym2);print(registeredSymbols.has(sym2)); // false
Tracking Symbol Usage
// Track where symbols are being usedfunction createSymbolTracker() { const usageMap = new WeakMap<symbol, Set<string>>();
return { track(sym: symbol, context: string): void { if (!usageMap.has(sym)) { usageMap.set(sym, new Set<string>()); }
usageMap.get(sym)!.add(context); },
getUsage(sym: symbol): string[] { if (!usageMap.has(sym)) { return []; }
return Array.from(usageMap.get(sym)!); } };}
// Usageconst tracker = createSymbolTracker();const configSymbol = Symbol("config");
tracker.track(configSymbol, "UserService");tracker.track(configSymbol, "AuthController");
print(tracker.getUsage(configSymbol)); // ["UserService", "AuthController"]
Feature Flags with Cleanup
// Manage feature flags with automatic cleanupfunction createFeatureManager() { const enabledFeatures = new WeakSet<symbol | object>(); const featureMetadata = new WeakMap<symbol | object, any>();
return { defineFeature(name: string, metadata: any = {}): symbol { const feature = Symbol.for(`feature:${name}`); featureMetadata.set(feature, { name, definedAt: Date.now(), ...metadata }); return feature; },
enableFeature(feature: symbol): void { enabledFeatures.add(feature); },
disableFeature(feature: symbol): void { enabledFeatures.delete(feature); },
isEnabled(feature: symbol): boolean { return enabledFeatures.has(feature); },
getMetadata(feature: symbol): any { return featureMetadata.get(feature); } };}
// Usageconst features = createFeatureManager();
const darkMode = features.defineFeature("dark-mode", { description: "Enable dark theme across the app", defaultValue: false});
features.enableFeature(darkMode);print(features.isEnabled(darkMode)); // trueprint(features.getMetadata(darkMode).description); // "Enable dark theme across the app"
Registry with Memory Management
// Create a registry that doesn't prevent garbage collectionfunction createRegistry<T>() { const items = new WeakMap<symbol | object, T>();
return { register(key: symbol | object, value: T): void { items.set(key, value); },
get(key: symbol | object): T | undefined { return items.get(key); },
unregister(key: symbol | object): boolean { return items.delete(key); },
has(key: symbol | object): boolean { return items.has(key); } };}
// Usageconst resourceRegistry = createRegistry<{ url: string, type: string }>();
const imageSymbol = Symbol("image-resource");resourceRegistry.register(imageSymbol, { url: "https://example.com/image.png", type: "image/png"});
const resource = resourceRegistry.get(imageSymbol);if (resource) { print(`Found resource: ${resource.url}`);}