Weird issue about JavaScript Proxy and getter functions

The two test cases blow both pass. I simply don’t understand the behavior. It seems that JavaScript Proxy cannot trap property getting inside a getter function.

test('JS Proxy normal method', () => {
  class Store {
    hidden = false;
    visible() {
      return !this.hidden;
    }
  }
  const accessList: PropertyKey[] = [];
  const proxy = new Proxy<Store>(new Store(), {
    get: (target: any, propertyKey: PropertyKey) => {
      accessList.push(propertyKey);
      return Reflect.get(target, propertyKey);
    },
  });
  expect(proxy.visible()).toBe(true);
  expect(accessList).toEqual(['visible', 'hidden']);
});

test('JS Proxy getter method', () => {
  class Store {
    hidden = false;
    get visible() {
      return !this.hidden;
    }
  }
  const accessList: PropertyKey[] = [];
  const proxy = new Proxy<Store>(new Store(), {
    get: (target: any, propertyKey: PropertyKey) => {
      accessList.push(propertyKey);
      return Reflect.get(target, propertyKey);
    },
  });
  expect(proxy.visible).toBe(true);
  expect(accessList).toEqual(['visible']);
});

Answer

You’re missing the receiver of the property access. The property might be defined on a different object than it is accessed on, and your Reflect.get call needs to take that into account. In particular, the receiver you get as a argument of the get trap is the proxy itself, and that’s also the object you want to evaluate the getter against, so that its this value refers to the proxy. However, Reflect.get(target, propertyKey) is the same as target[propertyKey], where the this value in the getter is set to the target and the .hidden property access can’t be detected by your proxy.