function createValidated(obj, schema) {
return new Proxy(obj, {
set(target, prop, value) {
if (schema[prop] && typeof value !== schema[prop]) {
throw new TypeError(`${prop} must be ${schema[prop]}`);
}
target[prop] = value; // directly writing β misses setters!
return true;
},
get(target, prop) {
return target[prop]; // directly reading β misses getters!
}
});
}
class User {
get displayName() { return 'User: ' + this._name; }
set name(v) { this._name = v.trim(); }
}
const u = new User();
const validated = createValidated(u, { _name: 'string' });
validated.name = ' Alice '; // setter should trim β but doesn't!function createValidated(obj, schema) {
return new Proxy(obj, {
set(target, prop, value, receiver) {
if (schema[prop] && typeof value !== schema[prop]) {
throw new TypeError(`${prop} must be ${schema[prop]}`);
}
// Reflect.set correctly handles prototype setters and receiver
return Reflect.set(target, prop, value, receiver);
},
get(target, prop, receiver) {
// Reflect.get correctly handles prototype getters and receiver
return Reflect.get(target, prop, receiver);
}
});
}
const u = new User();
const validated = createValidated(u, { _name: 'string' });
validated.name = ' Alice '; // setter trims: _name = 'Alice' β
console.log(validated.displayName); // 'User: Alice' βBug: target[prop] = value bypasses setter methods on the prototype. target[prop] for get bypasses getter methods. Always use Reflect inside Proxy traps.
Explanation: Reflect methods are designed to work correctly with prototype chains, getters/setters, and the receiver (this). Using target[prop] directly bypasses the prototype.
Key Insight: Always use Reflect.get/set/has/deleteProperty inside Proxy traps. They handle the receiver correctly, making getters and setters work as expected.