Fix ko.proxy deleteProperty trap dropping the property key#336
Fix ko.proxy deleteProperty trap dropping the property key#336brianmhunt merged 1 commit intomainfrom
Conversation
The deleteProperty handler bound only one parameter, `property`. Per the
Proxy spec, the trap is invoked with (target, property), so the handler
was actually receiving the internal `function(){}` target and attempting
to `delete mirror[stringifiedTarget]` / `delete object[stringifiedTarget]`.
Both are no-ops, so `delete proxied.foo` silently kept `foo` on both the
mirror store and the underlying object, and its tracked observable
stayed alive.
Bug latent since 2017 (4d1fe0d, original `ko.proxy` commit). No spec
exercised `delete` on a proxied object. Two subsequent commits (2dd14b3
adding `as any`; c8e2666 reformatting) preserved the bug because
neither engaged parameter semantics.
Fix: add the missing `_target` parameter. Adds a regression test that
`delete p.b` removes the property from both the proxy and the underlying
object.
Finding surfaced by phillipc in #297. All 2699 browser tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis change fixes a bug in Changes
Estimated Code Review Effort🎯 1 (Trivial) | ⏱️ ~5 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
First CI run failed with an unhandled exception: Unrelated to the proxy fix (only touched |
There was a problem hiding this comment.
Pull request overview
Fixes ko.proxy’s deleteProperty trap signature so the property key isn’t dropped, making delete proxied.foo actually remove the property and its mirror observable.
Changes:
- Fix
deletePropertytrap handler signature to accept(target, property)as per the Proxy spec. - Add a regression test covering
deletebehavior on proxied objects. - Add a changeset documenting the patch-level behavior fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/computed/src/proxy.ts | Corrects the deleteProperty trap parameters so deletions target the real key. |
| packages/computed/spec/proxyBehavior.ts | Adds regression coverage ensuring deletions affect both proxy and underlying object. |
| .changeset/fix-proxy-delete-property.md | Documents the behavioral bugfix for release notes/versioning. |
| delete mirror[property as any] | ||
| return delete object[property as any] |
There was a problem hiding this comment.
deleteProperty deletes from mirror before knowing whether deletion from the underlying object succeeded. If delete object[property] returns false (e.g., non-configurable property), the proxy ends up in an inconsistent state: has/get report the property missing while ownKeys and the underlying object still retain it. Consider deleting from object first and only deleting from mirror when the underlying delete succeeds (and return that boolean).
| delete mirror[property as any] | |
| return delete object[property as any] | |
| const deleted = delete object[property as any] | |
| if (deleted) { | |
| delete mirror[property as any] | |
| } | |
| return deleted |
Summary
The
deletePropertytrap on proxies built byko.proxy(packages/computed/src/proxy.ts) declared one parameter, namedproperty. Per the Proxy spec, the trap is invoked with(target, property)— so the handler was receiving the internalfunction(){}target in its one slot, stringifying it, and attempting todelete mirror[stringifiedTarget]/delete object[stringifiedTarget]. Both are no-ops. The real property key was discarded.Effect:
delete proxied.foosilently did nothing. The mirror kept the tracked observable alive, and the underlying object still had.foo.Latent since the original
ko.proxycommit in 2017. No test exerciseddeleteon a proxied object.Reproduction (pre-fix, stripped of TKO runtime)
Fix
One-line: add the missing
_targetpositional parameter sopropertyreceives the real key.Credit
Finding surfaced by @phillipc in #297 (round-2 TypeScript review findings, critical issue #1). Verified independently against current main and confirmed. Shipping as a standalone fix so #297's skill-adoption discussion can proceed without this bug blocking it.
Test plan
packages/computed/spec/proxyBehavior.ts— assertsdelete p.bremovesbfrom both the proxy and the underlying object.expect('b' in p).to.equal(false)fails because deleted property remains).proxyBehavior.ts.bunx tsc --noEmitclean.🤖 Generated with Claude Code
Summary by CodeRabbit