2016-12-06 46 views
0

這是一個難以解決的問題,我知道它是很多程序的問題(我將在最後詳細說明這一點)。 我想在typescript中創建一個自定義的setter,但被設置的屬性的數據類型不只是一個數字,字符串,布爾它實際上是一個類對象。這工作正常 - 但如果類實例的屬性被修改,那麼setter不叫。下面是這種情況的一個例子:(Typescript)Setter不在類實例的子屬性上調用

//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 

的問題是這個例子很清楚,但我只想說以下內容:

反正是有解決這個呢?因爲我已經從API,如Unity3D看到他們都選擇讓自己的「點」類只有私有成員,因此數據只能通過構造如設置:

//the 'Unity' solution 
transform.position = new Vector2(10, 10); //Okay 
transform.position.x = 20; //Error 

但是,這是不是在所有這是一個完美的解決方案,因爲從那時起,使得'Point'類的編程變得更加困難。

如果有人有一個技巧來解決這個問題,將不勝感激。

+0

你期待'bar.foo'的二傳手是兼容在執行'bar.foo.x'時調用? –

+0

我知道這不會發生,但我怎樣才能使它調用setter? @NitzanTomer –

+0

不可以。只有在爲屬性賦值時纔會調用setter,但在此處使用屬性的getter。你爲什麼想這樣做?你的目標是什麼? –

回答

0

僅當您爲屬性賦值時纔會使用setter。你可以繞過這一點的一種方法是使用Object.assign,就像這樣:

bar.foo = new Point(10, 10); 
bar.foo = Object.assign(bar.foo, {x: 20}) 

你也可以去更深:

bar.foo = Object.assign(bar.foo, {x: {z: 20} }) 
+0

回答OP問題的方式如何? –

+0

它將允許他在任何級別更改類實例中的屬性,同時還可以觸發setter。 – Shai

+0

答案的「更深入」部分只是爲了演示如何爲更復雜的對象工作,而不是專門針對OP的情況。 – Shai

1

您可以使用a Proxy爲:

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; 
      } 
     }); 
    } 
} 

code in playground

+0

哈哈,還沒有測試過,但這看起來像我正在尋找的答案。 (將標記爲答案,一旦我讀了它) –

+0

嗯我的tsc編譯器(我正在編譯ECMA腳本2015應該支持'代理')是說「無法找到名稱代理」? –

+0

對。 'Proxy'是es6的一個特性,如果你不是針對es6,那麼它就是從定義文件中丟失的。 –

0

關於打字稿的好處是,2種如果它們具有相同的形狀則是兼容的。因此,根據你所描述的,我覺得這似乎符合這個要求,這將不會破壞你的代碼,因爲這裏PointStorePoint

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 

} 
+0

但這會破壞我的代碼庫的其餘部分的兼容性。而且我不想從點類中捕獲事件 - 它需要從另一個類讓它說'PointStore'@Dummy –

+0

那麼爲什麼你不做一些像我編輯的答案?任何你需要在屬性更改時進行自定義處理的地方,只需使用那個類 – Dummy

+0

我的意思是說,但我需要再次從'PointWrapper'類中捕獲事件。 –

相關問題