Converting to v3 from v2? Check out the upgrade guide.
Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization techniques.
Immutable
provides List
, Stack
, Map
,
OrderedMap
, and Set
by using persistent hash maps tries
and vector tries
as popularized by Clojure and Scala. They achieve efficiency on modern
JavaScript VMs by using structural sharing and minimizing the need to copy or
cache data.
Immutable
also provides a lazy Seq
, allowing efficient
chaining of collection methods like map
and filter
without creating
intermediate representations. Create some Seq
with Range
and Repeat
.
Install immutable
using npm.
npm install immutable
Then require it into any module.
var Immutable = require('immutable');
var map = Immutable.Map({a:1, b:2, c:3});
To use immutable
from a browser, download dist/immutable.min.js
or use a CDN such as CDNJS
or jsDelivr.
Then, add it as a script tag to your page:
<script src="immutable.min.js"></script>
<script>
var map = Immutable.Map({a:1, b:2, c:3});
map = map.set('b', 20);
map.get('b'); // 20
</script>
Or use an AMD loader (such as RequireJS):
require(['./immutable.min.js'], function (Immutable) {
var map = Immutable.Map({a:1, b:2, c:3});
map = map.set('b', 20);
map.get('b'); // 20
});
If you're using browserify, the immutable
npm module
also works from the browser.
Use these Immutable collections and sequences as you would use native collections in your TypeScript programs while still taking advantage of type generics, error detection, and auto-complete in your IDE.
Just add a reference with a relative path to the type declarations at the top of your file.
///<reference path='./node_modules/immutable/dist/Immutable.d.ts'/>
import Immutable = require('immutable');
var map: Immutable.Map<string, number>;
map = Immutable.Map({a:1, b:2, c:3});
map = map.set('b', 20);
map.get('b'); // 20
Much of what makes application development difficult is tracking mutation and maintaining state. Developing with immutable data encourages you to think differently about how data flows through your application.
Subscribing to data events throughout your application, by using
Object.observe
, or any other mechanism, creates a huge overhead of
book-keeping which can hurt performance, sometimes dramatically, and creates
opportunities for areas of your application to get out of sync with each other
due to easy to make programmer error. Since immutable data never changes,
subscribing to changes throughout the model is a dead-end and new data can only
ever be passed from above.
This model of data flow aligns well with the architecture of React and especially well with an application designed using the ideas of Flux.
When data is passed from above rather than being subscribed to, and you're only
interested in doing work when something has changed, you can use equality.
Immutable
always returns itself when a mutation results in an identical
collection, allowing for using ===
equality to determine if something
has changed.
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 2);
assert(map1 === map2);
If an object is immutable, it can be "copied" simply by making another reference to it instead of copying the entire object. Because a reference is much smaller than the object itself, this results in memory savings and a potential boost in execution speed for programs which rely on copies (such as an undo-stack).
var map1 = Immutable.Map({a:1, b:2, c:3});
var clone = map1;
While immutable
is inspired by Clojure, Scala, Haskell and other functional
programming environments, it's designed to bring these powerful concepts to
JavaScript, and therefore has an Object-Oriented API that closely mirrors that
of ES6 Array,
Map, and
Set.
The difference for the immutable collections is that methods which would mutate
the collection, like push
, set
, unshift
or splice
instead return a new
immutable collection. Methods which return new arrays like slice
or concat
instead return new immutable collections.
var list1 = Immutable.List.of(1, 2);
var list2 = list1.push(3, 4, 5);
var list3 = list2.unshift(0);
var list4 = list1.concat(list2, list3);
assert(list1.size === 2);
assert(list2.size === 5);
assert(list3.size === 6);
assert(list4.size === 13);
assert(list4.get(0) === 1);
Almost all of the methods on Array
will be found in similar form on
Immutable.List
, those of Map
found on Immutable.Map
, and those of Set
found on Immutable.Set
, including collection operations like forEach()
and map()
.
var alpha = Immutable.Map({a:1, b:2, c:3, d:4});
alpha.map((v, k) => k.toUpperCase()).join();
// 'A,B,C,D'
Designed to inter-operate with your existing JavaScript, immutable
accepts plain JavaScript Arrays and Objects anywhere a method expects an
Iterable
with no performance penalty.
var map1 = Immutable.Map({a:1, b:2, c:3, d:4});
var map2 = Immutable.Map({c:10, a:20, t:30});
var obj = {d:100, o:200, g:300};
var map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
This is possible because immutable
can treat any JavaScript Array or Object
as an Iterable. You can take advantage of this in order to get sophisticated
collection methods on JavaScript Objects, which otherwise have a very sparse
native API. Because Seq evaluates lazily and does not cache intermediate
results, these operations can be extremely efficient.
var myObject = {a:1,b:2,c:3};
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 }
All immutable
Iterables can be converted to plain JavaScript Arrays and
Objects shallowly with toArray()
and toObject()
or deeply with toJS()
.
All Immutable Iterables also implement toJSON()
allowing them to be passed to
JSON.stringify
directly.
var deep = Immutable.Map({ a: 1, b: 2, c: Immutable.List.of(3, 4, 5) });
deep.toObject() // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
deep.toArray() // [ 1, 2, List [ 3, 4, 5 ] ]
deep.toJS() // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep) // '{"a":1,"b":2,"c":[3,4,5]}'
The collections in immutable
are intended to be nested, allowing for deep
trees of data, similar to JSON.
var nested = Immutable.fromJS({a:{b:{c:[3,4,5]}}});
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }
A few power-tools allow for reading and operating on nested data. The
most useful are mergeDeep
, getIn
, setIn
, and updateIn
, found on List
,
Map
and OrderedMap
.
var nested2 = nested.mergeDeep({a:{b:{d:6}}});
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }
nested2.getIn(['a', 'b', 'd']); // 6
var nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }
var nested4 = nested3.updateIn(['a', 'b', 'c'], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }
Seq
describes a lazy operation, allowing them to efficiently chain
use of all the Iterable methods (such as map
and filter
).
Seq is immutable — Once a Seq is created, it cannot be changed, appended to, rearranged or otherwise modified. Instead, any mutative method called on a Seq will return a new Seq.
Seq is lazy — Seq does as little work as necessary to respond to any method call.
For example, the following does not perform any work, because the resulting Seq is never used:
var oddSquares = Immutable.Sequence.of(1,2,3,4,5,6,7,8)
.filter(x => x % 2).map(x => x * x);
Once the Seq is used, it performs only the work necessary. In this example, no intermediate arrays are ever created, filter is called three times twice, and map is only called two times:
console.log(oddSquares.get(1)); // 9
Any collection can be converted to a lazy Seq with .toSeq()
.
var seq = Immutable.Map({a:1, b:1, c:1}).toSeq();
Seq allow for the efficient chaining of sequence operations, especially when converting to a different concrete type (such as to a JS object):
seq.flip().map(key => key.toUpperCase()).flip().toObject();
// Map { A: 1, B: 1, C: 1 }
As well as expressing logic that would otherwise seem memory-limited:
Immutable.Range(1, Infinity)
.skip(1000)
.map(n => -n)
.filter(n => n % 2 === 0)
.take(2)
.reduce((r, n) => r * n, 1);
// 1006008
Note: An iterable is always iterated in the same order, however that order may
not always be well defined, as is the case for the Map
.
Immutable
provides equality which treats immutable data structures as pure
data, performing a deep equality check if necessary.
var map1 = Immutable.Map({a:1, b:1, c:1});
var map2 = Immutable.Map({a:1, b:1, c:1});
assert(map1 !== map2);
assert(Immutable.is(map1, map2) === true);
Immutable.is()
uses the same measure of equality as Object.is
including if both are immutable and all keys and values are equal
using the same measure of equality.
If a tree falls in the woods, does it make a sound?
If a pure function mutates some local data in order to produce an immutable return value, is that ok?
— Rich Hickey, Clojure
Applying a mutation to create a new immutable object results in some overhead,
which can add up to a performance penalty. If you need to apply a series of
mutations locally before returning, Immutable
gives you the ability to create
a temporary mutable (transient) copy of a collection and apply a batch of
mutations in a performant manner by using withMutations
. In fact, this is
exactly how Immutable
applies complex mutations itself.
As an example, building list2
results in the creation of 1, not 3, new
immutable Lists.
var list1 = Immutable.List.of(1,2,3);
var list2 = list1.withMutations(function (list) {
list.push(4).push(5).push(6);
});
assert(list1.size === 3);
assert(list2.size === 6);
Note: immutable
also provides asMutable
and asImmutable
, but only
encourages their use when withMutations
will not suffice. Use caution to not
return a mutable copy, which could result in undesired behavior.
All documentation is contained within the type definition file, Immutable.d.ts.
Use Github issues for requests.
We actively welcome pull requests, learn how to contribute.
Hugh Jackson, for providing the npm package name. If you're looking for his unsupported package, see v1.4.1.
Phil Bagwell, for his inspiration and research in persistent data structures.
Immutable
is BSD-licensed. We also provide an additional patent grant.