2

I have an object that I want to intercept access to any of its properties, and if it does not exists to return a fallback value. I tried the following implementation, but for some reason, I get back a Proxy object instead of the fallback value.

const isObject = obj => typeof obj === 'object';
const hasKey = (obj, key) => key in obj;
const isNullOrUndefined = obj => obj === null || obj === undefined;

const obj = {
  keyOne: ''
};


const returnPropertyOnObject = (target, property) => {
  if (isObject(target[property])) {
    return safe(target[property])
  }

  return target[property];
}

function safe(obj) {
  return new Proxy(obj, {
    get: (target, property) => {

      if (hasKey(target, property) && !isNullOrUndefined(target[property])) {
        returnPropertyOnObject(target, property)
      }

      return new Proxy({}, {
        get: function(target, property) {
          return 'MISSING!!';
        }
      });;
    }
  });
}

const wrap = safe(obj);

console.log(wrap.notExists);
console.log(wrap.notExists.deep.nested.nested);

Moreover, I sometimes get properties like toJSON, or toString inside the Proxy.

What am I missing?

1 Answer 1

3

Once you return missing from the inner proxy, the chain is broken and the next dot will return bare undefined. You have to return the stub object itself from its get. To provide a default value, override toPrimitive:

undef = new Proxy({}, {
    get(_, prop) {
        // support some standard object methods, feel free to add more
        
        if (prop === Symbol.toPrimitive)
            return () => 'no such thing'
        if (prop === 'toString')
            return () => 'whatever you want'
        if (prop === 'toJSON')
            return () => 'whatever you want'

        // if asked for a custom prop, return itself
        
        return undef;
    }
});


const safe = obj => new Proxy(obj, {
    get(target, property) {
        let v = target[property];

        if (v === null || typeof v === 'undefined')
            return undef;

        if (typeof v === 'object')
            return safe(v);

        return v;
    }
});

const wrap = safe({foo: {bar: 'hey'}});

console.log(wrap.foo.bar + "!");
console.log(wrap.notExists.xx.yy.zz + "!");

Symbol.toPrimitive is a symbol that specifies a function... called to convert an object to a corresponding primitive value.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive

Sign up to request clarification or add additional context in comments.

6 Comments

But how can I return a default value?
Cool. Can you explain, please?
@undefined: added the doc link. Essentially, it's the same as old-style toString/valueOf thing.
But why it throws when I try to call toString? wrap.foo.bar.toString()
yep, this is because undef.get returns undef itself for all props, including toString. If you want undef to have standard object methods, you have to include them in get, like we did with Symbol.toPrimitive
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.