TypeScript:使用Property Decorator时得到错误的对象

时间:2019-06-23 16:07:42

标签: typescript decorator typescript-decorator

我有一个Car类,其中包含一些汽车包含板的信息,现在我想验证属性板,因此我以这种方式使用属性装饰器:

class Car{
    @validate 
    public plate: string;

    public model: string;
    // extra info

    constructor(plate: string, model: string){ 
        this.plate= plate; 
        this.model = model;
    }

    toString(): string{
       return `Car: ${this.plate} - ${this.model}`;
    }
}

然后我具有以下属性装饰器功能:

function validate(target: any, propertyKey: string){
    let value = target[propertyKey];
    Object.defineProperty(target, propertyKey, {
       get: () => value,
       set: (newValue) => {            
           const pattern = /^[A-Z]{2}\s?[0-9]{3}\s?[A-Z]{2}$/;
           if(pattern.test(newValue)){
               value = newValue;
           } else {
               console.error('Non valid plate: ', newValue);
               //value = undefined;
           }
       }
   })
}

现在,如果我以这种方式测试代码:

const car1 = new Car('IT123UE', 'Car1Model');
console.log('1 - ', car1.toString());

const car2 = new Car('IT000000UE', 'Car2Model');
console.log('2 - ', car2.toString());

我得到:

1 - Car: IT123UE - Car1Model
Non valid plate:  IT000000UE
2 - Car: IT123UE - Car2Model  <-- why print car1.plate if car2.plate is not valid and this is car2 object?

我已经使用value = undefined解决了;在验证函数内部的else中,但我不知道为什么在car2.plate中获得car1.plate值。 另一个测试,如果我更改顺序:

const car2 = new Car('IT000000UE', 'Car2Model');
console.log('2 - ', car2.toString());

const car1 = new Car('IT123UE', 'Car1Model');
console.log('1 - ', car1.toString());

我得到:

Non valid plate:  IT000000UE
2 -  Car: undefined - Car2Model <- now is undefinied
1 -  Car: IT123UE - Car1Model

我期望什么,但是为什么以前没有奏效?

我将TS 3.4.5与VS Code一起使用,并且在tsconfig中,我有

"target": "es6",
"experimentalDecorators": true,     

1 个答案:

答案 0 :(得分:1)

  

如果car2.plate无效并且这是car2对象,为什么打印car1.plate?

简短的答案:原因是两个类实例都在访问相同的类原型属性,并且原型属性是类实例之间的共享状态。

更长的答案:您的validate函数是实例属性上的property decorator。 TypeScript文档说实例属性装饰器将类原型作为第一个参数。结果,在验证器内部,您正在设置类原型的plate属性,而不是特定类实例的属性。由于类实例共享类原型,因此第二个类实例访问第一个实例已设置的属性值。

这里是一个演示了两种方法的演示; 第二种方法将为您服务。首先是您最初对共享原型状态所做的操作。第二个(validateToo)不使用共享状态; get / set而不是对target进行操作,而对this进行操作,并且get / set成为箭头函数,而不是箭头函数,因此它们采用正确的this对象。 / p>

class Car {
    @validate
    public plate: string;

    @validateToo
    public plateToo: string;

    constructor(plate: string) {
        this.plate = plate;
        this.plateToo = plate;
    }
}

// this second approach will work for you
function validateToo(target: any, propertyKey: string) {
    const fieldKey = `_${propertyKey}`;
    Object.defineProperty(target, propertyKey, {
        get() {
            return this[fieldKey];
        },
        set(newValue) {
            // you can put your validation logic here
            this[fieldKey] = newValue;
        }
    })
}

function validate(target: any, propertyKey: string) {
    let value = target[propertyKey];
    Object.defineProperty(target, propertyKey, {
        get: () => value,
        set: (newValue) => value = newValue
    })
}

演示

const car1 = new Car('one');
const car2 = new Car('two');
const car3 = new Car('three');

console.log(car1.plate, car1.plateToo); // three, one
console.log(car2.plate, car2.plateToo); // three, two
console.log(car3.plate, car3.plateToo); // three, three