Requires TypeScript 2.1.
This library uses structure sharing and lazy immutablility in an effort to maximize performance
via the Proxy object. If your environment does not support Proxy
, the library will fallback to deep-freezing
(walking the structure and applying Object.freeze()
).
This library works best with plain-old JS objects, arrays and Date()
objects. While it is aware of Map
and Set
and will deep-freeze those objects, it is recommended you use ImmutableJS
should you require more advanced immutable data structures.
IMPORTANT. Because of structure sharing and the nature of Proxy
, modifying the original data structures will
modify the Concrete objects. Once you generate an immutable object with Concrete, discard the orginial.
Concrete objects can be used just like their original objects. ConcreteObject
and ConcreteArray
do have some additional
methods.
If a program attempts to modify an object, a ReadonlyError
will be thrown.
A note on the LazyValue type: type LazyValue<V> = () => V
. Most obj.$
methods return the value wrapped
in a function. This is to prevent unnecessary evaluations of the lazy immutability.
Install the library.
npm i -S concrete-types
For TypeScript and ES6:
import * as Concrete from "concrete-types";
For NodeJS:
const Concrete = require("concrete-types");
For old-school script tag importing:
<script src="node_modules/concrete-types/bundle/concrete.min.js"></script>
Examples
Plain JS Object:
interface IObj {
foo: string;
bar: number;
fooBar: {
foo: boolean;
}
}
const obj: IObj = {
foo: "foo value",
bar: 123,
fooBar: {
foo: "fooBar.foo value"
}
};
// Generates a ConcreteObject.
const cobj = Concrete.from(obj);
// Or, skip the danger of the mutable original...
const cobj = Concrete.from<IObj>({
foo: "foo value",
bar: 123,
fooBar: {
foo: "fooBar.foo value"
}
});
// Use it like the original.
const anotherObj: IObj = cobj;
cobj.foo = "new value"; // Fail
delete cobj.foo; // Fail
// Shallow copy the source object into the target, overriding any existing properties.
const merged = cobj.$.assign({ newParam: 3 });
// Walk through the value/key pairs. Note LazyValue.
cobj.$.forEach((v, k, i, o) => {
console.log(`value=${v()} key=${k} index=${i} cobj=${o}`);
});
// Walk through the object, returning a transformed value. No LazyValue.
cobj.$.flatMap((v, k, i, o) => k !== "fooBar" ? v + v : v);
// Create a new ConcreteObject.
cobj.$.map(o => ({
newFoo: `${o.foo} is new`,
newBar: o.bar * 2
}));
// Pick out the given properties and create a new ConcreteObject.
const fooBarObj = cobj.$.pick("foo", "bar");
// Pluck out the values of the given properties and return a ConcreteArray.
const fooBarArray = cobj.$.pluck("foo", "bar");
// Convert the ConcreteObject into an array of key/value tuples:
// [["foo": "foo value", "bar": 123, "fooBar": {...}]]
const zipped = cobj.$.zip();
// Convert an array of key/value tuples into a ConcreteObject.
const cobj2: IObj = zipped.unzip();
// Deep copy to a mutable object.
const obj2 = cobj.$.toMutable();
Plain JS Array:
const arr = [1, 2, 3, 4, 5];
const carr = Concrete.from(arr);
// Fail: copyWithin, fill, pop, push, reverse, shift, sort, splice, unshift
carr[1] = 3; // Fail
carr.push(6); // Fail
// All other Array methods can be used, plus "sum()" and "toMutable()".
const total = carr.sum();
// Deep copy to a mutable array.
const arr2 = carr.toMutable();
Coming Soon. Hopefully the examples are enough the get rolling for now.
If you are insatiably curious, dig into the interface definitions under /src/interfaces
.
Coming very soon.