Can I extend Proxy with an ES2015 class?

Well, I had forgotten about this question, but someone recently upvoted it. Even though you technically can't extend a proxy, there is a way to force a class to instantiate as a proxy and force all its subclasses to instantiate as proxies with the same property descriptor functions (I've only tested this in Chrome):

class ExtendableProxy {
    constructor() {
        return new Proxy(this, {
            set: (object, key, value, proxy) => {
                object[key] = value;
                console.log('PROXY SET');
                return true;
            }
        });
    }
}

class ChildProxyClass extends ExtendableProxy {}

let myProxy = new ChildProxyClass();

// Should set myProxy.a to 3 and print 'PROXY SET' to the console:
myProxy.a = 3;

No, an ES2015 class cannot extend Proxy1.

Proxy objects have very atypical semantics and are considered "exotic objects" in ES2015, meaning that they do "not have the default behaviour for one or more of the essential internal methods that must be supported by all objects". They do not have any prototype, which is where you'd normally get most of the behaviour for a type you're extending. From section 26.2.2: "Properties of the Proxy Constructor" in the specification:

The Proxy constructor does not have a prototype property because proxy exotic objects do not have a [[Prototype]] internal slot that requires initialization.

This is not a limitation of Babel. If you attempt to extend Proxy in Chrome, where it and the class syntax are both natively supported, you'll still get a similar error:

Uncaught TypeError: Class extends value does not have valid prototype property undefined

1 "No" is the practical answer. However, Alexander O'Mara pointed out that if you assign a value to Proxy.prototype (gross!), it does become possible to extend, at least in some browsers. We experimented with this a little. Due to the behaviour of exotic Proxy instances this can't be used to accomplish much more than you could do with a function wrapping the constructor, and some behaviour does not appear to be consistent between browsers (I'm not sure what the specification expects if you do this). Please don't attempt anything like this in serious code.


From @John L. self response:
Inside constructor we can use Proxy to wrap the newly created instance. No need to extend Proxy.

Example, provide an observed point from an existing Point class:

class Point {

    constructor(x, y) {
        this.x = x
        this.y = y
    }

    get length() {
        let { x, y } = this
        return Math.sqrt(x * x + y * y)
    }

}

class ObservedPoint extends Point {

    constructor(x, y) {

        super(x, y)

        return new Proxy(this, {
            set(object, key, value, proxy) {
                if (object[key] === value)
                    return
                console.log('Point is modified')
                object[key] = value
            }
        })
    }
}

test:

p = new ObservedPoint(3, 4)

console.log(p instanceof Point) // true
console.log(p instanceof ObservedPoint) // true

console.log(p.length) // 5

p.x = 10 // "Point is modified"

console.log(p.length) // 5

p.x = 10 // nothing (skip)

class c1 {
    constructor(){
        this.__proto__  = new Proxy({}, {
            set: function (t, k, v) {
                t[k] = v;
                console.log(t, k, v);
            }
        });
    }
}

d = new c1(); d.a = 123;