2016-12-06 3 views
0

Это сложный вопрос, который, как я знаю, является проблемой для множества программ (я расскажу об этом в конце). Я хочу создать пользовательский сеттер в машинописном тексте, но тип данных устанавливаемого свойства - это не просто число, строка, bool, это фактически объект класса. Это отлично работает, но если свойство экземпляра класса модифицировано, то установщик не вызывается. Вот пример такой ситуации:(Typcript) Сеттер не вызывается в подсобстве экземпляра класса

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

Проблема довольно ясно из приведенного примера, но я просто хочу сказать следующее:

Есть ли вообще обойти это вообще? Потому что я видел из интерфейсов, таких как Unity3D они решили сделать класс их «точка» есть только частные члены и поэтому данные могут быть установлены только через конструктор например:

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

Но это не совсем идеальное решение проблемы, поскольку с тех пор программирование с классом «Point» намного сложнее.

Если у кого-то есть трюк, чтобы решить эту проблему, было бы весьма полезно.

+0

Вы ожидали сеттер 'bar.foo' быть вызывается при выполнении 'bar.foo.x'? –

+0

Я знаю, что этого не произойдет, но как я могу заставить его вызвать сеттера? @NitzanTomer –

+0

№. Setter вызывается только при назначении значения для свойства, но здесь вы используете getter для свойства. Почему вы хотите это сделать? Какова ваша цель? –

ответ

0

Сеттер будет использоваться только при назначении значения для свойства. Один из способов обойти это с помощью 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

Это позволит ему изменять свойства экземпляра класса на любом уровне, а также запускает сеттер. – 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 Script 2015, который должен поддерживать «прокси») говорит «Не могу найти прокси-сервер»? –

+0

Справа. «Proxy» - это функция es6, если вы не настроите таргетинг на es6, тогда она отсутствует в файле определения. –

0

Великая вещь в машинописном тексте состоит в том, что 2 типа совместимы, если они имеют одинаковую форму. Итак, основываясь на том, что вы описали, я думаю, что это, кажется, отвечает всем требованиям, и это не нарушит ваш код, потому что здесь PointStore совместим с Point класса

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». –