Reputation:
this is a difficult question that I know is a problem for lots of programs (I will elaborate on this at the end). I want to create a custom setter in typescript but the datatype of the property being set is not just a number, string, bool it is actually a class object. This works fine - but if a property of the class instance is modified then the setter is not called. Here is an example of such a situation:
//This class contains two properties
class Point
{
public x : number;
public y : number;
constructor(x : number, y : number) { this.x = x; this.y = 0; }
}
//How here is another class that contains a Point
//But it is private and a getter/setter is used
class PointStore
{
private _foo : Point;
public get foo() : Point { return this._foo; }
//Here is the problem, the setter is only called when the whole of foo is changed
public set foo(n : Point) { this._foo = n; console.log("Foo has been set!"); }
constructor() { this._foo = new Point(0, 0); }
}
//Use case
let bar : PointStore = new PointStore();
bar.foo = new Point(10, 10); //Logs "Foo has been set!"
bar.foo.x = 20; //Doesn't log anything
The problem is pretty clear from the example but I just want to say the following:
Is there anyway around this at all? Because I have seen from APIs such as Unity3D they have opted to make their 'Point' class have only private members and so data can only be set through the constructor e.g:
//the 'Unity' solution
transform.position = new Vector2(10, 10); //Okay
transform.position.x = 20; //Error
But this is not at all a perfect solution to the problem, as it makes programming with the 'Point' class much more difficult from then on.
If anyone has a trick to solve this, it would be greatly appreciated.
Upvotes: 1
Views: 3083
Reputation: 6814
The great thing about typescript is that, 2 types are compatible if they have the same shape. So, based on what you described, I think this seems to fit the bill and this will not break your code because PointStore
here is compatible with Point
class
class PointStore
{
private x : number;
private y : number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
};
public get x() { return this.point.x; };
public set x(x: number ) {
// your custom logic here
this.point.x = x;
};
// setter and getter for other y omitted
}
Upvotes: 0
Reputation: 164129
You can use a Proxy for that:
class PointStore {
private _foo: Point;
constructor() {
this.createProxy(new Point(0, 0));
}
public get foo(): Point { return this._foo; }
public set foo(point: Point) {
this.createProxy(point);
console.log("Foo has been set!");
}
private createProxy(point: Point) {
this._foo = new Proxy(point, {
set: function (target: Point, property: string, value: any) {
target[property] = value;
console.log("Foo has been set (using proxy)!");
return true;
}
});
}
}
Upvotes: 1
Reputation: 3872
The setter will be used only when you assign a value to the property. One way you can circumvent this is by using Object.assign
, like so:
bar.foo = new Point(10, 10);
bar.foo = Object.assign(bar.foo, {x: 20})
You can also go deeper:
bar.foo = Object.assign(bar.foo, {x: {z: 20} })
Upvotes: 1