Я хочу использовать immutable для реализации моей модели предметной области. Итак, я искал и искал и нашел хороший часто используемый шаблон, основанный на расширении Records.
Единственная проблема, которая у меня есть: я не могу заставить функцию .set
или .merge
работать должным образом.
Давайте воспользуемся следующим примером:
const SpecificRecord = Record({id: 0, someProp: ''});
export default class SpecificClass extends SpecificRecord {
id: number;
someProp: string;
constructor(id, someProp) {
super({id, someProp});
}
isSomePropFancy = () : boolean => {
return this.someProp === 'fancy';
}
}
Все идет нормально. Проблема начинается при попытке манипулировать записью:
const obj: SpecificClass = new SpecificClass(1, 'not-fancy');
console.info(obj.isFancy()); //prints out false
const newObj = obj.set('someProp', 'fancy'); //returns a Record -> should be SpecificClass
console.info(newObj.someProp) //prints out 'fancy'
console.info(newObj.isFancy()); //this fails: isFancy is not a function
У меня есть вопрос: как мы можем переопределить или любое другое решение, позволяющее нам обновлять расширение записи, сохраняя при этом правильный класс и все его методы?
Не для протокола: прямо сейчас я использую это, но я действительно очень очень не доволен этим решением:
//In SpecificClass I override:
set = (key, value): SpecificClass => {
const values = super.toObject();
values[key] = value;
return new SpecificClass(values);
};
Обычный совет для стрелочных функций: «если сомневаетесь, используйте его». В этом случае он бьет фабричные рекорды. Просто измените его на обычную функцию:
const SpecificRecord = Record({id: 0, someProp: ''});
class SpecificClass extends Immutable.Record({id: 0, someProp: ''}) {
constructor(id, someProp) {
super({id, someProp});
}
/* bad
isFancy = () => {
return this.someProp === 'fancy';
}
*/
// good
isFancy() {
return this.someProp === 'fancy';
}
}
const obj = new SpecificClass(1, 'not-fancy');
console.info('fancy:', obj.isFancy()); //prints out false
const newObj = obj.set('someProp', 'fancy'); //returns a Record -> should be SpecificClass
console.info('new obj prop:', newObj.someProp) //prints out 'fancy'
console.info('is new obj fancy?', newObj.isFancy());
ИМХО, методы экземпляра класса и записи действительно плохо сочетаются.
Методы записи (такие как .set
) не поддерживают цепочку прототипов, потому что они возвращают новую запись, как если бы вы снова вызвали фабрику. Проблема в том, что isSomePropFancy
определен в SpecificClass.prototype, поэтому вы потеряете к нему доступ при создании новой записи.
Вы можете использовать переопределение метода, как у вас, для каждого метода Record (хотя я не думаю, что ваш будет работать так, как он у вас есть):
const SpecificRecord = Record({id: 0, someProp: ''});
export default class SpecificClass extends SpecificRecord {
id: number;
someProp: string;
constructor(id, someProp) {
super({id, someProp});
}
isSomePropFancy = () : boolean => {
return this.someProp === 'fancy';
}
set = (key, value): SpecificClass => {
const values = {...this, [key]: value};
return new SpecificClass(values.id, values.someProp);
};
// also need to define merge, delete, etc.
}
или установить isSomePropFancy
в запись как свойство:
const SpecificRecord = Record({
id: 0,
someProp: '',
isSomePropFancy: (): boolean => this.someProp === 'fancy'
});
export default class SpecificClass extends SpecificRecord {
id: number;
someProp: string;
isSomePropFancy: () => boolean;
constructor(id, someProp) {
super({id, someProp});
}
}
В любом случае, в качестве примечания, я настоятельно рекомендую, чтобы конструктор принимал объект, а не отдельные параметры. Это делает внутреннюю логику и использование более простыми и удобными в сопровождении. Вам даже не нужно определять конструктор.
(например, new SpecificClass({id: 1, someProp: 'not-fancy'})
вместо new SpecificClass(1, 'not-fancy')
)
Мы используем TS и Records, и я убедил свою команду, что гораздо чище вообще не оборачивать типы Record в классы, а просто вводить фабрики как есть.
Вероятная причина сбоя: в Babel Stage 2 включен преобразование свойств класса, что превращает
isSomePropFancy
в статический метод класса.