javascriptecmascript-6proxy-pattern

ES6 Proxies - Is it possible to capture methods called upon a null object before they happen?


I'm working with an API that returns a schema for validating a form before users can submit their data.

For example, the schema has a User class featuring an attribute called email. If there is an error, User.validators.getEmailErrors() returns an Array of all the errors, e.g. ['Email address cannot be blank', 'Email addresses must match'].

However, if the field is valid, and no errors are found, getEmailErrors() returns null.

In my app, I want to safely chain more methods from getEmailErrors(), e.g. getEmailErrors().join(','), but without checking for null beforehand. Rather, is there a way, e.g. using ES6 proxies, to make getEmailAddress() aware of whether it will return an Array, and to safely ignore any methods like join() in case it returns null?

The easy solution would be to return an empty Array in the valid case instead of null, but assume I can't change that.


Solution

  • It can be done, indirectly.

    Following code was originated from HERE, and I add some code for testing.

    Thanks for the original author, Djamel Hassaine.

    {
        class test {
    		constructor () {
    			this.in = 0;
            }
            sum ( a, b ) {
                this.in += a + b;
    			return this;
            }
        }
        let k = new test();
    
        function traceMethodCalls(obj) {
            const handler = {
                get(target, propKey, receiver) {
                    console.log( target, propKey, receiver );
    				console.log( this );
    				console.log( handler === this );
    				const targetValue = Reflect.get(target, propKey, receiver);
                    if (typeof targetValue === 'function') {
                        return function (...args) {
                            console.log('CALL', propKey, args);
    						console.log( this );
    						console.log( this === receiver );
                            return targetValue.apply(this, args); // (A)
                        }
                    } else {
                        return targetValue;
                    }
                }
            };
            return new Proxy(obj, handler);    
        }
    
    	let l = traceMethodCalls( k );
    	console.log( l.sum( 1, 2 ) );
    	console.log( l );
    	console.log( l.sum( 1, 2 ) );
    	console.log( l );
    }

    Another way:

    User.validators.getEmailErrorsOriginal = User.validators.getEmailErrors
    User.validators.getEmailErrors = function ( ...args ) {
      return ( this.getEmailErrorsOriginal( ...args ) || [] );
    }