【问题标题】:TypeScript: get wrong object when use Property DecoratorTypeScript:使用属性装饰器时得到错误的对象
【发布时间】:2019-11-05 14:47:13
【问题描述】:

我有一个类 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,     

【问题讨论】:

    标签: typescript decorator typescript-decorator


    【解决方案1】:

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

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

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

    这是一个演示两种方法的演示; 第二种方法适合您。第一个是您最初对共享原型状态所做的事情。第二个(validateToo)不使用共享状态; get/set 不是在 target 上操作,而是在 this 上操作,get/set 不是箭头函数,而是函数,因此它们采用正确的 this 对象。

    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
    

    【讨论】:

    • 感谢您的解释。
    猜你喜欢
    • 2019-07-29
    • 2018-05-02
    • 1970-01-01
    • 2014-01-02
    • 2021-10-18
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    相关资源
    最近更新 更多