`Record.isRecord` returning true for Record Exotic Objects
acutmore opened this issue · 3 comments
Issue raised during the July 2022 TC39 plenary by @waldemarhorwat .
In the current spec: Record.isRecord(Object(#{}))
returns true, because for objects it checks the presence of the [[RecordData]]
internal slot.
This means that there are an infinite number of things for which Record.isRecord
would return true
, that can all have the same content (same properties, with same values) yet are not strictly equal to each other.
If code is branching using Record.isRecord
it possible that they are doing so because they are expecting a successful value to subsequently behave as a record (value based equality) - perhaps they plan to use it as a key in a Map
. They may not be aware that this predicate also returns true for the wrapper object.
A potential solution raised during the meeting was to remove Record.isRecord
and instead have Record.isRecordWrapper
that would return false
for a primitive record, and only return true for the Record Exotic Object.
A potential risk to this, IMO, is that a static method is highly visible to developers when they first start learning the language.
Potentially creating a 'forcing function' of learning about wrappers sooner than would usually be done, increasing cognitive load. It may also be confusing that there isn't an equivalent Number.isNumberWrapper
.
An alternative approach would be to address 'brand checking' of records as a separate proposal that is exploring a more consistent approach across all the primitives. For example as is done in the current pattern-matching spec of adding Boolean[@@matcher]
and Number[@@matcher]
etc.
Alternatively, for Tuple, brand checking could be done with Tuple.prototype.valueOf
, unfortunately we don't have a Record prototype to do the same there. We could propose similar functionality on the Record namespace through something like Record.recordValue
.
This solution could also enable what is discussed in #329 (comment)