如何在ImmutableJS Record扩展类上使用setter

时间:2019-06-11 14:23:49

标签: javascript overriding setter extends immutable.js

我想使用不可变来实现我的域模型。因此,我查看并搜索了基于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.log(obj.isFancy()); //prints out false
const newObj = obj.set('someProp', 'fancy'); //returns a Record -> should be SpecificClass
console.log(newObj.someProp) //prints out 'fancy'
console.log(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);
};

2 个答案:

答案 0 :(得分:2)

箭头功能的通常建议是“有疑问时使用”。在这种情况下,它将打破唱片工厂。只需将其更改为常规函数即可:

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.log('fancy:', obj.isFancy()); //prints out false
const newObj = obj.set('someProp', 'fancy'); //returns a Record -> should be SpecificClass
console.log('new obj prop:', newObj.someProp) //prints out 'fancy'
console.log('is new obj fancy?', newObj.isFancy());

答案 1 :(得分:0)

恕我直言,类实例方法和Records确实不能很好地融合。

Record方法(例如.set)不维护原型链,因为它们会返回新的Record,就像您再次调用工厂一样。问题在于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类型包装在类中,而直接按原样键入工厂,这要干净得多。