TypeScript: получить неправильный объект при использовании Property Decorator - PullRequest
1 голос
/ 23 июня 2019

У меня есть класс 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?

Я решил использовать значение = неопределенное;в остальном внутри моей функции проверки, но я не понимаю, почему в моем 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

, что я ожидал, но почему это не сработало раньше?

IЯ использую TS 3.4.5 с VS Code, а в tsconfig у меня есть

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

1 Ответ

1 голос
/ 24 июня 2019

зачем печатать car1.plate, если car2.plate недопустимо и это объект car2?

Краткий ответ : Причина в том, что оба экземпляра класса обращаются к одному и тому же свойству прототипа класса, и это свойство прототипа является общим состоянием между экземплярами класса.

Более длинный ответ : Ваша функция validate является декоратором свойства для свойства экземпляра. В документации TypeScript говорится, что декораторы свойств экземпляра получают прототип класса в качестве первого аргумента. В результате внутри вашего валидатора вы устанавливаете свойство plate прототипа класса, а не конкретного экземпляра класса. Поскольку экземпляры класса совместно используют прототип класса, второй экземпляр класса получает доступ к значению свойства, которое первый экземпляр уже установил.

Вот демонстрация, которая иллюстрирует два подхода; второй подход будет работать для вас . Первый - это то, что вы изначально делали с общим прототипом. Второй (validateToo) не использует общее состояние; вместо того, чтобы работать с target, get / set работает с 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
...