How do I make a custom immutable type using facebook/immutable-js?

这一生的挚爱 提交于 2020-01-25 00:29:08

问题


I am attempting to make a simple immutable Interval type. It has a start and an end property. Is there a straightforward way to utilize the immutable-js library to create your own custom immutable types?

My current attempt at using a containment pattern with Immutable.Map and Immutable.List is not working so well. difference, for example, should return an immutable list of Intervals, but because it creates new instances of Interval, the List it returns will not pass equality.

It feels like I am taking the wrong approach, but I'm not sure how. I could easily represent the data with Immutable.Map, but I want to include difference in its public interface, and not include general Map methods like get and set. i.e. I want it to be immutable, yet be its own type with its own interface.

var Immutable = require('immutable')

function Interval(start, end) {
  this.value = Immutable.Map({
    start: start,
    end: end
  })
}

Interval.prototype.start = function () {
  return this.value.get('start')
}

Interval.prototype.end = function () {
  return this.value.get('end')
}

Interval.prototype.equals = function(interval) {
  return this.value.equals(interval.value)
}

Interval.prototype.difference = function (subtrahend) {
  var nonoverlapping = subtrahend.start() > this.end() || subtrahend.end() < this.start()

  return nonoverlapping ? Immutable.List.of(this) :
    Immutable.List.of(
      new Interval(this.start(), subtrahend.start()),
      new Interval(subtrahend.end(), this.end())
    )
    .filter(function(interval) {
      return interval.end() > interval.start()
    })
}

Some tests:

it('should return an empty array for an exact difference', function() {
  var interval = new Interval(80, 90)
  interval.difference(interval)
    .equals(Immutable.List())
    .should.be.true
})

it('should return an array with the same Interval for a missed difference', function() {
  var interval = new Interval(80, 90)
  console.log(interval.difference(new Interval(50, 60)).toJS());
  interval.difference(new Interval(50, 60))
    .equals(Immutable.List.of([
      { value: Immutable.Map({ start: 80, end: 90 }) }
    ]))
    .should.be.true
})

回答1:


Why not use Record type:

var Interval = Immutable.Record({start: 0, end: 1})

This creates the record type for you, defining start and end properties with default values of 0 and 1, respectively.

Now to create instances of Interval, just do:

var newInterval = new Interval() //will have default values above
var newInterval = new Interval({start: 10, end: 30)

Note that instances of Record type are just value types, so they should just contain data. You can, of course, define functions that operate on this data value.

var difference = function (interval1, interval2) {
  var nonoverlapping = interval2.start > interval1.end || interval2.end < interval1.start

  return nonoverlapping ? Immutable.List.of(interval1) :
    Immutable.List([
      new Interval({start: interval1.start, end: interval2.start}),
      new Interval({start: interval2.end, end: interval1.end})
    ])
    .filter(function(interval) {
      return interval.end > interval.start
    });
}

Equality you get for free, using Immutable.is(interval1, interval2)

For more information on the record type: http://facebook.github.io/immutable-js/docs/#/Record




回答2:


You can use "extendable-immutable" library.

Then it is easy:

const {Map} = require('extendable-immutable');

class Interval extends Map {
    constructor(start, end) {
        super({
            value: new Map({start: start, end: end})
        });
    }

    start() {
        return this.get('value').get('start');
    }

    end() {
        return this.get('value').get('end');
    }

    //...
}

I do not recommend Record if you choose methods (such as start, end) over fields like me. If you implement it with Record you will have field(s) that are exposed automatically.

Instead of exposing a value field, you may want to create your Record custom class with _value field, however it is not always possible. See:

https://github.com/facebook/immutable-js/issues/970




回答3:


Implement the method valueOf.

For example:

Interval.prototype.valueOf = function() {
  return Map({ class: Interval, value: this.value });
}

and on the tests:

Immutable.is(new Interval(80, 90), new Interval(80, 90)); // true

Another option is to convert the class to a ValueObject by implementing the methods equals and hashCode (Use the functions Immutable.is and Immutable.hash for the implementation).



来源:https://stackoverflow.com/questions/31816069/how-do-i-make-a-custom-immutable-type-using-facebook-immutable-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!