{"id":13539092,"url":"https://github.com/tc39/proposal-symbols-as-weakmap-keys","last_synced_at":"2025-04-19T17:35:10.184Z","repository":{"id":39888208,"uuid":"264232471","full_name":"tc39/proposal-symbols-as-weakmap-keys","owner":"tc39","description":"Permit Symbols as keys in WeakMaps, entries in WeakSets and WeakRefs, and registered in FinalizationRegistries","archived":false,"fork":false,"pushed_at":"2022-10-11T13:15:46.000Z","size":76,"stargazers_count":89,"open_issues_count":6,"forks_count":7,"subscribers_count":25,"default_branch":"main","last_synced_at":"2025-03-29T10:51:19.611Z","etag":null,"topics":["membranes","realms","record","symbols","tc39","tuple","weakmap-keys"],"latest_commit_sha":null,"homepage":"http://tc39.es/proposal-symbols-as-weakmap-keys","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tc39.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-15T15:39:10.000Z","updated_at":"2024-12-08T08:28:56.000Z","dependencies_parsed_at":"2023-01-19T19:46:55.583Z","dependency_job_id":null,"html_url":"https://github.com/tc39/proposal-symbols-as-weakmap-keys","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-symbols-as-weakmap-keys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-symbols-as-weakmap-keys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-symbols-as-weakmap-keys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-symbols-as-weakmap-keys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-symbols-as-weakmap-keys/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249750294,"owners_count":21320110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["membranes","realms","record","symbols","tc39","tuple","weakmap-keys"],"created_at":"2024-08-01T09:01:20.136Z","updated_at":"2025-04-19T17:35:10.156Z","avatar_url":"https://github.com/tc39.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# Symbols as WeakMap keys\n\nStage 3\n\n**Coauthors/champions**:\n\n- Robin Ricard (@rricard)\n- Rick Button (@rickbutton)\n- Daniel Ehrenberg (@littledan)\n- Leo Balter (@leobalter)\n- Caridy Patiño (@caridy)\n- Rick Waldron (@rwaldron)\n- Ashley Claymore (@acutmore)\n\n[Spec text](https://tc39.es/proposal-symbols-as-weakmap-keys)\n\n---\n\n## Introduction\n\nThis proposal extends the WeakMap API to allow usage of unique Symbols as keys.\n\nCurrently, WeakMaps are limited to only allow objects as keys, and this is a limitation for WeakMaps as the goal is to have unique values that can be eventually GC'ed.\n\nSymbol is the only primitive type in ECMAScript that allows unique values. A symbol value - like the one produced by calling the `Symbol( [ description] )` expression - can only be identified with access to its original production. Any reproduction of the same expression - using the same value for description - will not restore the original value of any previous production. This is why we call the symbol values distinct.\n\nObjects are used as keys for WeakMaps because they share the same identity aspect. The identity of an object can only be verified with access to the original production, no new object will match a pre-existing one in - e.g. - a strict comparison.\n\n### Earlier discussions\n\nSee [earlier discussion](https://github.com/tc39/ecma262/issues/1194) on Symbols as WeakMap keys.\n\n### Draft PR\n\nSee the current [draft PR to ECMA-262](https://github.com/tc39/ecma262/pull/2777) with the proposed spec.\n\n## Use Cases\n\n### Easy to create and share keys\n\nInstead of requiring creating a new object to be only used as a key, a symbol would provide more clarity for the ergonomics of a WeakMap and the proper roles of its keys and mapped items.\n\n```javascript\nconst weak = new WeakMap();\n\n// Pun not intended: being a symbol makes it become a more symbolic key\nconst key = Symbol('my ref');\nconst someObject = { /* data data data */ };\n\nweak.set(key, someObject);\n```\n\n### ShadowRealms, Membranes, and Virtualization\n\nThe [ShadowRealms proposal](https://github.com/tc39/proposal-shadowrealm) disallows access to object values. For most virtualization cases, a membrane system is built on top of Realms-related API to connect references using WeakMaps. A Symbol value, being a primitive value, is still accessible, allowing membranes being structured with proper weakmaps using connected identities.\n\n```javascript\nconst objectLookup = new WeakMap();\nconst otherRealm = new ShadowRealm();\nconst coinFlip = otherRealm.evaluate(`(a, b) =\u003e Math.random() \u003e 0.5 ? a : b;`);\n\n// later...\nlet a = { name: 'alice' };\nlet b = { name: 'bob' };\nlet symbolA = Symbol();\nlet symbolB = Symbol();\nobjectLookup.set(symbolA, a);\nobjectLookup.set(symbolB, b);\na = b = null; // ok to drop direct object references\n\n// connected identities preserved as the symbols round-tripped through the other realm\nlet chosen = objectLookup.get(coinFlip(symbolA, symbolB));\nassert(['alice', 'bob'].includes(chosen.name));\n```\n\n### Record and Tuples\n\nThis proposal aims to solve a problem space introduced by the [Record \u0026 Tuple Proposal][rtp]; how can we reference and access non-primitive values in a primitive?\n\ntl;dr We see Symbols, dereferenced through WeakMaps, as the most reasonable way forward to reference Objects from Records and Tuples, given all the constraints raised in the discussion so far.\n\nThere are some open questions as to how this should they work exactly, and also valid ergonomics/ecosystem coordination issues, which we hope to resolve/validate in the course of the TC39 stage process. We'll start with an understanding of the problem space, including why Records and Tuples are a good first step without this feature. Then, we'll examine various possible solutions, with their pros and cons,\n\n[Records \u0026 Tuples][rtp] can't contain objects, functions, or methods and will throw a `TypeError` when someone attempts to do it:\n\n```js\nconst server = #{\n    port: 8080,\n    handler: function (req) { /* ... */ }, // TypeError!\n};\n```\n\nThis limitation exists because the one of the **key goals** of the [Record \u0026 Tuple Proposal][rtp]  is to have deep immutability guarantees and structural equality _by default_.\n\nThe userland solutions mentioned below provide multiple methods of side-stepping this limitation, and `Record and Tuple` is viable and useful without additional language support for boxing objects. This proposal attempts to describe solutions that complement the usage of these userland solutions with `Record and Tuple`, but is not a prerequisite to landing `Record and Tuple` in the language.\n\nAccepting Symbol values as WeakMap keys would allow JavaScript libraries to implement their own RefCollection-like things which could be reusable (avoiding the need to pass around the mapping all over the place, using a single global one, and just passing around [Records and Tuples](https://github.com/tc39/proposal-record-tuple)) while not leaking memory over time.\n\n```js\nclass RefBookkeeper {\n    #references = new WeakMap();\n    ref(obj) {\n        // (Simplified; we may want to return an existing symbol if it's already there)\n        const sym = Symbol();\n        this.#references.set(sym, obj);\n        return sym;\n    }\n    deref(sym) { return this.#references.get(sym); }\n}\nglobalThis.refs = new RefBookkeeper();\n\n// Usage\nconst server = #{\n    port: 8080,\n    handler: refs.ref(function handler(req) { /* ... */ }),\n};\nrefs.deref(server.handler)({ /* ... */ });\n```\n\n## Well-known and Registered symbols as WeakMap keys\n\nSome TC39 delegates have argued strongly in either direction. We see both \"allowing\" and \"disallowing\" as acceptable options.\n\n### [Registered](https://tc39.es/ecma262/multipage#sec-symbol.for) symbols\n\nDisallowing registered symbols is discussed in [issue 21](https://github.com/tc39/proposal-symbols-as-weakmap-keys/issues/21).\n\n### [Well-Known](https://tc39.es/ecma262/multipage#sec-well-known-symbols) symbols\n\nAllowing well-known symbols doesn't seem so bad, since they are analogous to Objects that are held alive for the lifetime of the Realm. In the context of a Realm that stays alive as long as there is JS running (e.g., on the Web, the Realm of a Worker), things like `Symbol.iterator` are analogous to primordials like `Object.prototype` and `Array.prototype`. Just because these will stay alive doesn't mean we disallow them as WeakMap keys.\n\nWhile 'registered' symbols can be detected using `Symbol.keyFor`, there is currently no built in predicate to test if a symbol is 'well-known' or not. If 'well-known' symbols were not allowed as keys in a WeakMap code would need to ensure it handles this potential abrupt completion.\n\n## Support for Symbols in WeakRefs and FinalizationRegistry\n\nWe should also support Symbols in WeakRefs and FinalizationRegistry. Not only is this consistent with Objects as WeakMap keys but it also enables user-land to build/demonstrate more advanced functionality. e.g. WeakMaps that support Records/Tuples as keys.\n\n## Summing up\n\nWe think that adding Symbols as WeakMap keys is a useful, minimal primitive enabling Records and Tuples to reference Objects while respecting the constraints imposed by the goal to support membrane-based isolation within a single Realm. At the same time, the userspace solutions seem sufficient for many/most use cases; we believe that Records and Tuples are very useful without any additional mechanism for referencing objects from primitives, and therefore makes sense to proceed with Records and Tuples independently of this proposal.\n\n[rtp]: https://github.com/tc39/proposal-record-tuple\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-symbols-as-weakmap-keys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-symbols-as-weakmap-keys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-symbols-as-weakmap-keys/lists"}