Accessor And Super

Topics: Language Specification
Dec 25, 2012 at 11:02 PM

When defining getter and setter in a class, and overriding them in a subclass you often want to reuse the logic defined in the base class getter/setter, to do so i thought i could use the keyword super like that : 

class A {
	private _property:string;
	
	public get property():string {
		return this._property;
	}
	
	public set property(value:string) {
		this._property = value;
	} 
	
	public showMyValue() {
		alert(this._property);
	}

}

class B extends A {
	public get property():string {
		return super.property;
	}
	
	public set property(value:string) {
		super.property = value;
	}
}

var t:B = new B();
t.property = "value";
var t2:B = new B();

t.showMyValue() //"value"
t2.showMyValue() //"value"

However like that examples demonstrate it the property is set on the prototype of the base class. Firstly this behavior is not really intuitive and could lead to bug, then without the possibility of reusing code logic of base class accessors, getter and setters become a lot less powerful.

Coordinator
Dec 28, 2012 at 7:29 PM

JavaScript's prototype based inheritance can be a source of confusion at times.

The interesting problem here is that you are shadowing the base class by using the same names in the derived class, so you need to call 'super' to access the base instance, but prefixing the call with "super" means the 'this' is the super in the call, so you're modifying the_property on the super prototype object.  To make things trickier yet, as you are using getters/setters these appear as properties, and you can't "call" the function, thus proving the right 'this' for the invocation.

One option is to refactor out the logic you want to reuse in the base class, so that you can call this from either class without needing to disambiguate (and even if you did, as it's a method you could specify the 'this', i.e. ( super.setHelper.call(this) ).

Does something like the below work?

 

class A {
	private _property:string;

	setHelper(val: string) {
	   return val.toUpperCase();
	}
	
	public get property():string {
		return this._property;
	}
	
	public set property(value:string) {
		this._property = this.setHelper(value);
	} 

	public showMyValue() {
		console.log(this._property);
	}

}

class B extends A {
    private _property;

    public get property(): string {
        return this._property;
    }

    public set property(value: string) {
        this._property = this.setHelper(value) + "plus";
    }
}

var a: A = new A();
a.property = "setA";
var b:B = new B();
b.property = "setB";
var b2:B = new B();
b2.property = "setB2";

a.showMyValue();  // "SETA"
b.showMyValue();  // "SETBplus"
b2.showMyValue(); // "SETB2plus" 

Dec 29, 2012 at 11:49 PM
Edited Dec 30, 2012 at 9:33 PM

While something like that could work, firstly that implies that the base class have been wrote with in mind the possibility of overriding the properties getter/setter, then redeclaring the private property like it to in fact give the access to our subclass don't seems very clean in my point of view.

What about translating my first example to something like it :

 

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var __getSuperProperty = this.__getSuperProperty || function (target,_super,property) {
    var descriptor = Object.getOwnPropertyDescriptor(_super.prototype,property);
    if(typeof descriptor.get != 'undefined')   {
        return descriptor.get.call(target)
    }
    return target[property];
}
var __setSuperProperty = this.__setSuperProperty || function (target,_super,property,value) {
    var descriptor = Object.getOwnPropertyDescriptor(_super.prototype,property);
    if(typeof descriptor.set != 'undefined')   {
        descriptor.set.call(target,value);
        return;
    }
    target[property] = value;
}
var A = (function () {
    function A() { }
    Object.defineProperty(A.prototype, "property", {
        get: function () {
            return this._property;
        },
        set: function (value) {
            this._property = value;
        },
        enumerable: true,
        configurable: true
    });
    A.prototype.showMyValue = function () {
        alert(this._property);
    };
    return A;
})();
var B = (function (_super) {
    __extends(B, _super);
    function B() {
        _super.apply(this, arguments);

    }
    Object.defineProperty(B.prototype, "property", {
        get: function () {
            return __getSuperProperty(this,_super,"property");
        },
        set: function (value) {
            __setSuperProperty(this,_super,"property",value);
        },
        enumerable: true,
        configurable: true
    });
    return B;
})(A);
var t = new B();
t.property = "value";
var t2 = new B();
t.showMyValue(); // value
t2.showMyValue(); // undefined

 

This way we use  the getter/setter function described in the base class in the right context if such a method exist.

 

 
Jan 6, 2013 at 1:12 PM
Edited Jan 7, 2013 at 9:18 AM

My bad, the solution i proposed does not work when the super class does not define getter/setter.
Perhaps the logic should be implemented at compile time ?
In any way i really think that this problem is huge drawback to the getter/setter system, firstly because it leads to unexpected results, then it makes a lot's of pattern unusable.

Jan 27, 2013 at 11:51 AM

I am trying to reuse base-class setter/getter and I can't. I hope this part will be improved.

Jan 11 at 9:20 PM
Is there a plan to allow virtualized base class accessors/setters in the future? Seems like an imbalance in the language, as compared to methods.