ImmutableAssign
Lightweight immutable helper that supports TypeScript type checking, and allows you to continue working with POJO (Plain Old JavaScript Object).
This library is trying to solve following problems:
- Most immutable JavaScript libraries try to encapsulate the data, and provide proprietary APIs to work with the data. They are more verbose than normal JavaScript syntax. E.g., map1.get('b') vs map1.b, nested2.getIn(['a', 'b', 'd']) vs nested2.a.b.d, etc.
- Encapsulated data is no more POJO, therefore cannot be easily used with other libraries, e.g., lodash, underscore, etc.
- seamless-immutable address some of above issues when reading the properties, but still use verbose APIs to write properties.
- Immutability Helpers allows us work with POJO, but it has still introduced some magic keywords, such as $set, $push, etc.
- Most importantly (in my opinion), we lost TypeScript type checking. E.g., when calling nested2.getIn(['a', 'b', 'd']), TypeScript won't be able to warn me if I changed property 'd' to 'e'.
This library has only one method iassign(), which accept a POJO object and return you a new POJO object with specific property updated.
##Install with npm
npm install immutable-assign --save
Function Signature
function iassign<TObj, TProp, TContext>(
obj: TObj,
getProp: (obj: TObj, context: TContext) => TProp,
setProp: (prop: TProp) => TProp,
ctx?: TContext): TObj;
####Example 1: Update nested property
var iassign = require("immutable-assign");
var deepFreeze = require("deep-freeze");
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} };
deepFreeze(o1);
var o2 = iassign(
o1,
function (o) { return o.a.b.c[0][0]; },
function (ci) { ci.d++; return ci; }
);
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} });
expect(o2.a.b.c[0][0].d).toBe(12);
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]);
expect(o2.a.b.c[0][0]).not.toBe(o1.a.b.c[0][0]);
expect(o2.a.b.c[0][0].d).not.toBe(o1.a.b.c[0][0].d);
expect(o2.a2).toBe(o1.a2);
expect(o2.a.b2).toBe(o1.a.b2);
expect(o2.a.b.c2).toBe(o1.a.b.c2);
expect(o2.a.b.c[0][0].e).toBe(o1.a.b.c[0][0].e);
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]);
####Example 2: Update array
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} };
deepFreeze(o1);
var o2 = iassign(
o1,
function (o) { return o.a.b.c[1]; },
function (c) { c.push(101); return c; }
);
expect(o1).toEqual({ a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]], c2: {} }, b2: {} }, a2: {} });
expect(o2.a.b.c[1][1]).toBe(101);
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[1]).not.toBe(o1.a.b.c[1]);
expect(o2.a2).toBe(o1.a2);
expect(o2.a.b2).toBe(o1.a.b2);
expect(o2.a.b.c2).toBe(o1.a.b.c2);
expect(o2.a.b.c[0]).toBe(o1.a.b.c[0]);
expect(o2.a.b.c[0][0]).toBe(o1.a.b.c[0][0]);
expect(o2.a.b.c[1][0]).toBe(o1.a.b.c[1][0]);
####Example 3: Update nested property, referring to external context.
var o1 = { a: { b: { c: [[{ d: 11, e: 12 }], [{ d: 21, e: 22 }]] } } };
deepFreeze(o1);
var p1 = { a: 0 };
var o2 = iassign(
o1,
function (o, ctx) { return o.a.b.c[ctx.p1.a][0]; },
function (ci) { ci.d++; return ci; },
{ p1: p1 }
);
####Example 4: Work with 3rd party libraries, e.g., lodash
var iassign = require("immutable-assign");
var deepFreeze = require("deep-freeze");
var _ = require("lodash");
var o1 = { a: { b: { c: [1, 2, 3] } } };
deepFreeze(o1);
var o2 = iassign(
o1,
function (o) { return o.a.b.c; },
function (c) {
return _.map(c, function (i) { return i + 1; });
}
);
expect(o1).toEqual({ a: { b: { c: [1, 2, 3] } } });
expect(o2.a.b.c).toEqual([2, 3, 4]);
expect(o2).not.toBe(o1);
expect(o2.a).not.toBe(o1.a);
expect(o2.a.b).not.toBe(o1.a.b);
expect(o2.a.b.c).not.toBe(o1.a.b.c);
expect(o2.a.b.c[0]).not.toBe(o1.a.b.c[0]);
##Limitations and Constraints
-
getProp() function must be pure, it cannot access anything other than the input parameters. I.e., it must not access "this" or "window" objects. In addition, it must not modify the input parameters. It should only return a property that needs to be updated.
-
Current version does not support following characters in the property name:
- [].\
- e.g., { "propery [].\\": {} } is invalid