Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: get wrong object when use Property Decorator

I have a class Car with some info of a car include plate, now I want validate the property plate, so I use a property decoratorin this way:

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

Then I have the following property decorator function:

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

Now, if I test my code in this way:

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

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

I get:

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?

I have solved using value = undefined; in the else inside my validation function but I don't figure out why in my car2.plate I get car1.plate value. Another test, if I change the order:

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

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

I get:

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

what I expected, but why didn't it work before?

I'm using TS 3.4.5 with VS Code, and in tsconfig I have

"target": "es6",
"experimentalDecorators": true,     
like image 676
max Avatar asked Mar 01 '26 11:03

max


1 Answers

why print car1.plate if car2.plate is not valid and this is car2 object?

Short answer: The reason is that both class instances are accessing the same class prototype property, and that prototype property is shared state between the class instances.

Longer answer: Your validate function is a property decorator on an instance property. The TypeScript documentation says that instance property decorators receive the class prototype as the first argument. As a result, inside your validator, you are setting the plate property of the class prototype not of the specific class instance. Since class instances share the class prototype, the second class instance accesses the property value that the first instance already set.

Here is a demo that illustrates two approaches; the second approach will work for you. The first is what you originally did with shared prototype state. The second (validateToo) does not use shared state; instead of operating on target, the get/set operate on this, and instead of being arrow functions, the get/set become functions, so that they take the correct this object.

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

Demo

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
like image 103
Shaun Luttin Avatar answered Mar 04 '26 09:03

Shaun Luttin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!