I'm spinning an issue off from @rricard 's comment here: #23 (review)
I remember starting to discuss the different variants of helper functions that could land with this proposal, but I don't recall if we came to a clear answer or not as I failed to take detailed notes.
cc: @ljharb @syg @codehag
Some options for helpers:
A) WeakMap.isValidKey
et al
There would also be WeakSet.isValidValid
, WeakRef.isValidTarget
and FinalizationRegistry.isValidTarget
, which would likely all share the same function instance if the logic is the same. This predicate returning true
/false
would be a direct indication of if the same value would-not/would throw if attempted to be used with an instance of the class.
One issue that has been raised with this helper is that it is likely to 'lock' in the set of valid values; if the only time the logic for this function can change is to encompass new types when they are added to the language, and couldn't change (across versions) for existing types. For example: relaxing the rule to allow 'well-known' symbols in the future. This could impact Record&Tuple
, if the initial MVP spec for them says they are not valid WeakMap
keys, then this could mean they can never be, as the predicate would need to change its result.
B) Object.isUnforgeable
(Doesn't necessarily need to be on Object
). The key idea here, as I understand it, is to de-couple the predicate from if the value can be used in a weak collection. i.e. it can be used as an indicator but does not necessarily perfectly divide values into the two sets of valid/invalid WeakMap
keys.
This could potentially allow the set of valid WeakMap
keys to change as this predicate itself would always give the same result for a given input across versions. For example isUnforgeable(Symbol.iterator)
may always return false
**, but registered symbols may throw when used as WeakMap
keys in one version but then relaxed in future versions. Or for Record&Tuple
, isUnforgeable(Tuple(Symbol))
could always return true
, but maybe this does not guarantee that it is also a valid WeakMap
key, relaxing this could then be a future separate proposal.
**
isUnforgeable(Symbol.iterator) === false
may be wrong, it would depend on how we interpret forgeability.
// index.js
delete Array.prototype[Symbol.unscopables];
delete Symbol.prototype.constructor;
delete globalThis.Symbol;
// is `@@unscopables` now unreachable? maybe not as [HasBinding](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-object-environment-records-hasbinding-n) still references it
const unscopables = new ShadowRealm().evaluate(`Symbol.unscopables`); // have I re-forged it?
?) Other options?
Z) No new predicate functions
One option is to not introduce new predicates in the spec itself. This would leave userland to create their own predicates and/or use try/catch
.