Validating Properties Using the set Trap

Suppose you want to create an object whose property values must be numbers. That means every new property added to the object must be validated, and an error must be thrown if the value isn’t a number. To accomplish this, you could define a set trap that overrides the default behavior of setting a value. The set trap receives four arguments:

  1. trapTarget - the object that will receive the property (the proxy’s target)
  2. key - the property key (string or symbol) to write to
  3. value - the value being written to the property
  4. receiver - the object on which the operation took place (usually the proxy)

Reflect.set() is the set trap’s corresponding reflection method, and it’s the default behavior for this operation. The Reflect.set() method accepts the same four arguments as the set proxy trap, making the method easy to use inside of the trap. The trap should return true if the property was set or false if not. (The Reflect.set() method returns the correct value based on whether the operation succeeded.)

To validate the values of properties, you’d use the set trap and inspect the value that is passed in. Here’s an example:

  1. let target = {
  2. name: "target"
  3. };
  4. let proxy = new Proxy(target, {
  5. set(trapTarget, key, value, receiver) {
  6. // ignore existing properties so as not to affect them
  7. if (!trapTarget.hasOwnProperty(key)) {
  8. if (isNaN(value)) {
  9. throw new TypeError("Property must be a number.");
  10. }
  11. }
  12. // add the property
  13. return Reflect.set(trapTarget, key, value, receiver);
  14. }
  15. });
  16. // adding a new property
  17. proxy.count = 1;
  18. console.log(proxy.count); // 1
  19. console.log(target.count); // 1
  20. // you can assign to name because it exists on target already
  21. proxy.name = "proxy";
  22. console.log(proxy.name); // "proxy"
  23. console.log(target.name); // "proxy"
  24. // throws an error
  25. proxy.anotherName = "proxy";

This code defines a proxy trap that validates the value of any new property added to target. When proxy.count = 1 is executed, the set trap is called. The trapTarget value is equal to target, key is "count", value is 1, and receiver (not used in this example) is proxy. There is no existing property named count in target, so the proxy validates value by passing it to isNaN(). If the result is NaN, then the property value is not numeric and an error is thrown. Since this code sets count to 1, the proxy calls Reflect.set() with the same four arguments that were passed to the trap to add the new property.

When proxy.name is assigned a string, the operation completes successfully. Since target already has a name property, that property is omitted from the validation check by calling the trapTarget.hasOwnProperty() method. This ensures that previously-existing non-numeric property values are still supported.

When proxy.anotherName is assigned a string, however, an error is thrown. The anotherName property doesn’t exist on the target, so its value needs to be validated. During validation, the error is thrown because "proxy" isn’t a numeric value.

Where the set proxy trap lets you intercept when properties are being written to, the get proxy trap lets you intercept when properties are being read.