if { x } .= y currently compiles to a key-presence test:
if (y && typeof y === 'object' && 'x' in y) {
let { x } = y;
...
}
So if { x } .= { x: undefined } enters the block with x === undefined. switch y; when { x } uses the same semantics, intentionally — object pattern matching is shape-based.
In if context the key-presence test is surprising. The natural reading is "if I can pull a usable x out of y, do this," and undefined values silently passing the guard has caused at least one real bug.
Proposal
In if-form destructuring, require each bound name to be != null before entering:
if (y && typeof y === 'object' && y.x != null) {
let { x } = y;
...
}
Rationale:
!= null (not full truthiness) preserves 0/""/false as legitimate values — same tradeoff if x := y already makes for the single-name case.
- Matches the intuitive read of "extract these values, only if they're set."
Open questions
- Apply only to
if-form, or also to switch when { x }? Diverging keeps switch as pure shape-match; aligning keeps the two siblings consistent.
- Breaking change for any code relying on the current key-presence-with-
undefined behavior — likely rare but worth scanning the codebase.
- Nested patterns (
if { a: { b } } .= y) — recurse the check, or only top-level?
if { x } .= ycurrently compiles to a key-presence test:So
if { x } .= { x: undefined }enters the block withx === undefined.switch y; when { x }uses the same semantics, intentionally — object pattern matching is shape-based.In
ifcontext the key-presence test is surprising. The natural reading is "if I can pull a usablexout ofy, do this," andundefinedvalues silently passing the guard has caused at least one real bug.Proposal
In
if-form destructuring, require each bound name to be!= nullbefore entering:Rationale:
!= null(not full truthiness) preserves0/""/falseas legitimate values — same tradeoffif x := yalready makes for the single-name case.Open questions
if-form, or also toswitch when { x }? Diverging keepsswitchas pure shape-match; aligning keeps the two siblings consistent.undefinedbehavior — likely rare but worth scanning the codebase.if { a: { b } } .= y) — recurse the check, or only top-level?