Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

immutable

immutable-js61.5mMIT5.0.3TypeScript support: included

Immutable Data Collections

immutable, persistent, lazy, data, datastructure, functional, collection, stateless, sequence, iteration

readme

Immutable collections for JavaScript

Build Status Chat on slack

Read the docs and eat your vegetables.

Docs are automatically generated from README.md and immutable.d.ts. Please contribute! Also, don't miss the wiki which contains articles on additional specific topics. Can't find something? Open an issue.

Table of contents:

Introduction

Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.

Immutable.js provides many Persistent Immutable data structures including: List, Stack, Map, OrderedMap, Set, OrderedSet and Record.

These data structures are highly efficient on modern JavaScript VMs by using structural sharing via hash maps tries and vector tries as popularized by Clojure and Scala, minimizing the need to copy or cache data.

Immutable.js 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.

Want to hear more? Watch the presentation about Immutable.js:

Immutable Data and React

Getting started

Install immutable using npm.

# using npm
npm install immutable

# using Yarn
yarn add immutable

# using pnpm
pnpm add immutable

# using Bun
bun add immutable

Then require it into any module.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b') + ' vs. ' + map2.get('b'); // 2 vs. 50

Browser

Immutable.js has no dependencies, which makes it predictable to include in a Browser.

It's highly recommended to use a module bundler like webpack, rollup, or browserify. The immutable npm module works without any additional consideration. All examples throughout the documentation will assume use of this kind of tool.

Alternatively, Immutable.js may be directly included as a script tag. Download or link to a CDN such as CDNJS or jsDelivr.

Use a script tag to directly add Immutable to the global scope:

<script src="immutable.min.js"></script>
<script>
  var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
  var map2 = map1.set('b', 50);
  map1.get('b'); // 2
  map2.get('b'); // 50
</script>

Or use an AMD-style loader (such as RequireJS):

require(['./immutable.min.js'], function (Immutable) {
  var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
  var map2 = map1.set('b', 50);
  map1.get('b'); // 2
  map2.get('b'); // 50
});

Flow & TypeScript

Use these Immutable collections and sequences as you would use native collections in your Flowtype or TypeScript programs while still taking advantage of type generics, error detection, and auto-complete in your IDE.

Installing immutable via npm brings with it type definitions for Flow (v0.55.0 or higher) and TypeScript (v2.1.0 or higher), so you shouldn't need to do anything at all!

Using TypeScript with Immutable.js v4

Immutable.js type definitions embrace ES2015. While Immutable.js itself supports legacy browsers and environments, its type definitions require TypeScript's 2015 lib. Include either "target": "es2015" or "lib": "es2015" in your tsconfig.json, or provide --target es2015 or --lib es2015 to the tsc command.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b') + ' vs. ' + map2.get('b'); // 2 vs. 50

Using TypeScript with Immutable.js v3 and earlier:

Previous versions of Immutable.js include a reference file which you can include via relative path to the type definitions at the top of your file.

///<reference path='./node_modules/immutable/dist/immutable.d.ts'/>
import { Map } from 'immutable';
var map1: Map<string, number>;
map1 = Map({ a: 1, b: 2, c: 3 });
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50

The case for Immutability

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 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 collections should be treated as values rather than objects. While objects represent some thing which could change over time, a value represents the state of that thing at a particular instance of time. This principle is most important to understanding the appropriate use of immutable data. In order to treat Immutable.js collections as values, it's important to use the Immutable.is() function or .equals() method to determine value equality instead of the === operator which determines object reference identity.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
map1.equals(map2); // true
map1 === map2; // false

Note: As a performance optimization Immutable.js attempts to return the existing collection when an operation would result in an identical collection, allowing for using === reference equality to determine if something definitely has not changed. This can be extremely useful when used within a memoization function which would prefer to re-run the function if a deeper equality check could potentially be more costly. The === equality check is also used internally by Immutable.is and .equals() as a performance optimization.

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 2); // Set to same value
map1 === map2; // true

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).

const { Map } = require('immutable');
const map = Map({ a: 1, b: 2, c: 3 });
const mapCopy = map; // Look, "copies" are free!

JavaScript-first API

While Immutable.js 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 ES2015 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.

const { List } = require('immutable');
const list1 = List([1, 2]);
const list2 = list1.push(3, 4, 5);
const list3 = list2.unshift(0);
const list4 = list1.concat(list2, list3);
assert.equal(list1.size, 2);
assert.equal(list2.size, 5);
assert.equal(list3.size, 6);
assert.equal(list4.size, 13);
assert.equal(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().

const { Map } = require('immutable');
const alpha = Map({ a: 1, b: 2, c: 3, d: 4 });
alpha.map((v, k) => k.toUpperCase()).join();
// 'A,B,C,D'

Convert from raw JavaScript objects and arrays.

Designed to inter-operate with your existing JavaScript, Immutable.js accepts plain JavaScript Arrays and Objects anywhere a method expects a Collection.

const { Map, List } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
const map2 = Map({ c: 10, a: 20, t: 30 });
const obj = { d: 100, o: 200, g: 300 };
const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }
const list1 = List([1, 2, 3]);
const list2 = List([4, 5, 6]);
const array = [7, 8, 9];
const list3 = list1.concat(list2, array);
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

This is possible because Immutable.js can treat any JavaScript Array or Object as a Collection. 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.

const { Seq } = require('immutable');
const myObject = { a: 1, b: 2, c: 3 };
Seq(myObject)
  .map(x => x * x)
  .toObject();
// { a: 1, b: 4, c: 9 }

Keep in mind, when using JS objects to construct Immutable Maps, that JavaScript Object properties are always strings, even if written in a quote-less shorthand, while Immutable Maps accept keys of any type.

const { fromJS } = require('immutable');

const obj = { 1: 'one' };
console.log(Object.keys(obj)); // [ "1" ]
console.log(obj['1'], obj[1]); // "one", "one"

const map = fromJS(obj);
console.log(map.get('1'), map.get(1)); // "one", undefined

Property access for JavaScript Objects first converts the key to a string, but since Immutable Map keys can be of any type the argument to get() is not altered.

Converts back to raw JavaScript objects.

All Immutable.js Collections can be converted to plain JavaScript Arrays and Objects shallowly with toArray() and toObject() or deeply with toJS(). All Immutable Collections also implement toJSON() allowing them to be passed to JSON.stringify directly. They also respect the custom toJSON() methods of nested objects.

const { Map, List } = require('immutable');
const deep = Map({ a: 1, b: 2, c: List([3, 4, 5]) });
console.log(deep.toObject()); // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
console.log(deep.toArray()); // [ 1, 2, List [ 3, 4, 5 ] ]
console.log(deep.toJS()); // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep); // '{"a":1,"b":2,"c":[3,4,5]}'

Embraces ES2015

Immutable.js supports all JavaScript environments, including legacy browsers (even IE11). However it also takes advantage of features added to JavaScript in ES2015, the latest standard version of JavaScript, including Iterators, Arrow Functions, Classes, and Modules. It's inspired by the native Map and Set collections added to ES2015.

All examples in the Documentation are presented in ES2015. To run in all browsers, they need to be translated to ES5.

// ES2015
const mapped = foo.map(x => x * x);
// ES5
var mapped = foo.map(function (x) {
  return x * x;
});

All Immutable.js collections are Iterable, which allows them to be used anywhere an Iterable is expected, such as when spreading into an Array.

const { List } = require('immutable');
const aList = List([1, 2, 3]);
const anArray = [0, ...aList, 4, 5]; // [ 0, 1, 2, 3, 4, 5 ]

Note: A Collection is always iterated in the same order, however that order may not always be well defined, as is the case for the Map and Set.

Nested Structures

The collections in Immutable.js are intended to be nested, allowing for deep trees of data, similar to JSON.

const { fromJS } = require('immutable');
const nested = 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.

const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });

const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }

console.log(nested2.getIn(['a', 'b', 'd'])); // 6

const nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }

const nested4 = nested3.updateIn(['a', 'b', 'c'], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }

Equality treats Collections as Values

Immutable.js collections are treated as pure data values. Two immutable collections are considered value equal (via .equals() or is()) if they represent the same collection of values. This differs from JavaScript's typical reference equal (via === or ==) for Objects and Arrays which only determines if two variables represent references to the same object instance.

Consider the example below where two identical Map instances are not reference equal but are value equal.

// First consider:
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 2, c: 3 };
obj1 !== obj2; // two different instances are always not equal with ===

const { Map, is } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
map1 !== map2; // two different instances are not reference-equal
map1.equals(map2); // but are value-equal if they have the same values
is(map1, map2); // alternatively can use the is() function

Value equality allows Immutable.js collections to be used as keys in Maps or values in Sets, and retrieved with different but equivalent collections:

const { Map, Set } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = Map({ a: 1, b: 2, c: 3 });
const set = Set().add(map1);
set.has(map2); // true because these are value-equal

Note: is() uses the same measure of equality as Object.is for scalar strings and numbers, but uses value equality for Immutable collections, determining if both are immutable and all keys and values are equal using the same measure of equality.

Performance tradeoffs

While value equality is useful in many circumstances, it has different performance characteristics than reference equality. Understanding these tradeoffs may help you decide which to use in each case, especially when used to memoize some operation.

When comparing two collections, value equality may require considering every item in each collection, on an O(N) time complexity. For large collections of values, this could become a costly operation. Though if the two are not equal and hardly similar, the inequality is determined very quickly. In contrast, when comparing two collections with reference equality, only the initial references to memory need to be compared which is not based on the size of the collections, which has an O(1) time complexity. Checking reference equality is always very fast, however just because two collections are not reference-equal does not rule out the possibility that they may be value-equal.

Return self on no-op optimization

When possible, Immutable.js avoids creating new objects for updates where no change in value occurred, to allow for efficient reference equality checking to quickly determine if no change occurred.

const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = originalMap.set('b', 2);
updatedMap === originalMap; // No-op .set() returned the original reference.

However updates which do result in a change will return a new reference. Each of these operations occur independently, so two similar updates will not return the same reference:

const { Map } = require('immutable');
const originalMap = Map({ a: 1, b: 2, c: 3 });
const updatedMap = originalMap.set('b', 1000);
// New instance, leaving the original immutable.
updatedMap !== originalMap;
const anotherUpdatedMap = originalMap.set('b', 1000);
// Despite both the results of the same operation, each created a new reference.
anotherUpdatedMap !== updatedMap;
// However the two are value equal.
anotherUpdatedMap.equals(updatedMap);

Batching Mutations

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 minor performance penalty. If you need to apply a series of mutations locally before returning, Immutable.js 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.js applies complex mutations itself.

As an example, building list2 results in the creation of 1, not 3, new immutable Lists.

const { List } = require('immutable');
const list1 = List([1, 2, 3]);
const list2 = list1.withMutations(function (list) {
  list.push(4).push(5).push(6);
});
assert.equal(list1.size, 3);
assert.equal(list2.size, 6);

Note: Immutable.js 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.

Important!: Only a select few methods can be used in withMutations including set, push and pop. These methods can be applied directly against a persistent data-structure where other methods like map, filter, sort, and splice will always return new immutable data-structures and never mutate a mutable collection.

Lazy Seq

Seq describes a lazy operation, allowing them to efficiently chain use of all the higher-order collection methods (such as map and filter) by not creating intermediate collections.

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 lazySeq does as little work as necessary to respond to any method call. Values are often created during iteration, including implicit iteration when reducing or converting to a concrete data structure such as a List or JavaScript Array.

For example, the following performs no work, because the resulting Seq's values are never iterated:

const { Seq } = require('immutable');
const oddSquares = Seq([1, 2, 3, 4, 5, 6, 7, 8])
  .filter(x => x % 2 !== 0)
  .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, and map is only called once:

oddSquares.get(1); // 9

Any collection can be converted to a lazy Seq with Seq().

const { Map, Seq } = require('immutable');
const map = Map({ a: 1, b: 2, c: 3 });
const lazySeq = Seq(map);

Seq allows for the efficient chaining of operations, allowing for the expression of logic that can otherwise be very tedious:

lazySeq
  .flip()
  .map(key => key.toUpperCase())
  .flip();
// Seq { A: 1, B: 2, C: 3 }

As well as expressing logic that would otherwise seem memory or time limited, for example Range is a special kind of Lazy sequence.

const { Range } = require('immutable');
Range(1, Infinity)
  .skip(1000)
  .map(n => -n)
  .filter(n => n % 2 === 0)
  .take(2)
  .reduce((r, n) => r * n, 1);
// 1006008

Comparison of filter(), groupBy(), and partition()

The filter(), groupBy(), and partition() methods are similar in that they all divide a collection into parts based on applying a function to each element. All three call the predicate or grouping function once for each item in the input collection. All three return zero or more collections of the same type as their input. The returned collections are always distinct from the input (according to ===), even if the contents are identical.

Of these methods, filter() is the only one that is lazy and the only one which discards items from the input collection. It is the simplest to use, and the fact that it returns exactly one collection makes it easy to combine with other methods to form a pipeline of operations.

The partition() method is similar to an eager version of filter(), but it returns two collections; the first contains the items that would have been discarded by filter(), and the second contains the items that would have been kept. It always returns an array of exactly two collections, which can make it easier to use than groupBy(). Compared to making two separate calls to filter(), partition() makes half as many calls it the predicate passed to it.

The groupBy() method is a more generalized version of partition() that can group by an arbitrary function rather than just a predicate. It returns a map with zero or more entries, where the keys are the values returned by the grouping function, and the values are nonempty collections of the corresponding arguments. Although groupBy() is more powerful than partition(), it can be harder to use because it is not always possible predict in advance how many entries the returned map will have and what their keys will be.

Summary filter partition groupBy
ease of use easiest moderate hardest
generality least moderate most
laziness lazy eager eager
# of returned sub-collections 1 2 0 or more
sub-collections may be empty yes yes no
can discard items yes no no
wrapping container none array Map/OrderedMap

Additional Tools and Resources

  • Atom-store

    • A Clojure-inspired atom implementation in Javascript with configurability for external persistance.
  • Chai Immutable

    • If you are using the Chai Assertion Library, this provides a set of assertions to use against Immutable.js collections.
  • Fantasy-land

    • Specification for interoperability of common algebraic structures in JavaScript.
  • Immutagen

    • A library for simulating immutable generators in JavaScript.
  • Immutable-cursor

    • Immutable cursors incorporating the Immutable.js interface over Clojure-inspired atom.
  • Immutable-ext

    • Fantasyland extensions for immutablejs
  • Immutable-js-tools

    • Util tools for immutable.js
  • Immutable-Redux

    • redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state.
  • Immutable-Treeutils

    • Functional tree traversal helpers for ImmutableJS data structures.
  • Irecord

    • An immutable store that exposes an RxJS observable. Great for React.
  • Mudash

    • Lodash wrapper providing Immutable.JS support.
  • React-Immutable-PropTypes

    • PropType validators that work with Immutable.js.
  • Redux-Immutablejs

    • Redux Immutable facilities.
  • Rxstate

    • Simple opinionated state management library based on RxJS and Immutable.js.
  • Transit-Immutable-js

    • Transit serialisation for Immutable.js.
    • See also: Transit-js

Have an additional tool designed to work with Immutable.js? Submit a PR to add it to this list in alphabetical order.

Contributing

Use Github issues for requests.

We actively welcome pull requests, learn how to contribute.

Immutable.js is maintained within the Contributor Covenant's Code of Conduct.

Changelog

Changes are tracked as Github releases.

License

Immutable.js is MIT-licensed.

Thanks

Phil Bagwell, for his inspiration and research in persistent data structures.

Hugh Jackson, for providing the npm package name. If you're looking for his unsupported package, see this repository.

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. Dates are formatted as YYYY-MM-DD.

Unreleased

[5.0.0]

Breaking changes

To sum up, the big change in 5.0 is a Typescript change related to Map that is typed closer to the JS object. This is a huge change for TS users, but do not impact the runtime behavior. (see Improve TypeScript definition for Map for more details)

Other breaking changes are:

[BREAKING] Remove deprecated methods:

Released in 5.0.0-rc.1

  • Map.of('k', 'v'): use Map([ [ 'k', 'v' ] ]) or Map({ k: 'v' })
  • Collection.isIterable: use isIterable directly
  • Collection.isKeyed: use isKeyed directly
  • Collection.isIndexed: use isIndexed directly
  • Collection.isAssociative: use isAssociative directly
  • Collection.isOrdered: use isOrdered directly

[BREAKING] OrdererMap and OrderedSet hashCode implementation has been fixed

Released in 5.0.0-rc.1

Fix issue implementation of hashCode for OrdererMap and OrderedSet where equal objects might not return the same hashCode.

Changed in #2005

[BREAKING] Range function needs at least two defined parameters

Released in 5.0.0-beta.5

Range with undefined would end in an infinite loop. Now, you need to define at least the start and end values.

If you need an infinite range, you can use Range(0, Infinity).

Changed in #1967 by @jdeniau

[Minor BC break] Remove default export

Released in 5.0.0-beta.1

Immutable does not export a default object containing all it's API anymore. As a drawback, you can not immport Immutable directly:

- import Immutable from 'immutable';
+ import { List, Map } from 'immutable';

- const l = Immutable.List([Immutable.Map({ a: 'A' })]);
+ const l = List([Map({ a: 'A' })]);

If you want the non-recommanded, but shorter migration path, you can do this:

- import Immutable from 'immutable';
+ import * as Immutable from 'immutable';

  const l = Immutable.List([Immutable.Map({ a: 'A' })]);

[TypeScript Break] Improve TypeScript definition for Map

Released in 5.0.0-beta.1

If you do use TypeScript, then this change does not impact you : no runtime change here. But if you use Map with TypeScript, this is a HUGE change ! Imagine the following code

const m = Map({ length: 3, 1: 'one' });

This was previously typed as Map<string, string | number>

and return type of m.get('length') or m.get('inexistant') was typed as string | number | undefined.

This made Map really unusable with TypeScript.

Now the Map is typed like this:

MapOf<{
    length: number;
    1: string;
}>

and the return type of m.get('length') is typed as number.

The return of m.get('inexistant') throw the TypeScript error:

Argument of type '"inexistant"' is not assignable to parameter of type '1 | "length"

If you want to keep the old definition

This is a minor BC for TS users, so if you want to keep the old definition, you can declare you Map like this:

const m = Map<string, string | number>({ length: 3, 1: 'one' });
If you need to type the Map with a larger definition

You might want to declare a wider definition, you can type your Map like this:

type MyMapType = {
  length: number;
  1: string | null;
  optionalProperty?: string;
};
const m = Map<MyMapType>({ length: 3, 1: 'one' });

Keep in mind that the MapOf will try to be consistant with the simple TypeScript object, so you can not do this:

Map({ a: 'a' }).set('b', 'b');
Map({ a: 'a' }).delete('a');

Like a simple object, it will only work if the type is forced:

Map<{ a: string; b?: string }>({ a: 'a' }).set('b', 'b'); // b is forced in type and optional
Map<{ a?: string }>({ a: 'a' }).delete('a'); // you can only delete an optional key
Are all Map methods implemented ?

For now, only get, getIn, set, update, delete, remove, toJS, toJSON methods are implemented. All other methods will fallback to the basic Map definition. Other method definition will be added later, but as some might be really complex, we prefer the progressive enhancement on the most used functions.

Fixes

Internal

  • [Internal] Migrating TS type tests from dtslint to TSTyche #1988 and #1991 by @mrazauskas. Special thanks to @arnfaldur that migrated every type tests to tsd just before that.
  • [internal] Upgrade to rollup 3.x #1965 by @jdeniau
  • [internal] upgrade tooling (TS, eslint) and documentation packages: #1971, #1972, #1973, #1974, #1975, #1976, #1977, #1978, #1979, #1980, #1981

[4.3.7] - 2024-07-22

  • Fix issue with slice negative of filtered sequence #2006 by @jdeniau

[4.3.6] - 2024-05-13

  • Fix Repeat(<value>).equals(undefined) incorrectly returning true #1994 by @butchler

[4.3.5] - 2024-01-16

[4.3.4] - 2023-08-25

  • Rollback toJS type due to circular reference error #1958 by @jdeniau

[4.3.3] - 2023-08-23

[4.3.2] - 2023-08-01

  • [TypeScript] Fix isOrderedSet type #1948

[4.3.1] - 2023-07-11

  • Faster and implementation of some #1944
  • [internal] remove unused exports #1928

[4.3.0] - 2023-03-10

[4.2.4] - 2023-02-06

  • [TypeScript] Improve type infererence for from JS by KSXGitHub #1927

[4.2.3] - 2023-02-02

  • [TypeScript] groupBy return either a Map or an OrderedMap: make the type more precise than base Collection #1924

[4.2.2] - 2023-01-02

  • [Flow] Add type for partition method #1920 by Dagur

[4.2.1] - 2022-12-23

  • [Typescript] rollback some of the change on toJS to avoir circular reference

[4.2.0] - 2022-12-22

  • [TypeScript] Better type for toJS #1917 by jdeniau
    • [TS Minor Break] tests are ran with TS > 4.5 only. It was tested with TS > 2.1 previously, but we want to level up TS types with recent features. TS 4.5 has been released more than one year before this release. If it does break your implementation (it might not), you should probably consider upgrading to the latest TS version.
  • Added a partition method to all containers #1916 by johnw42

[4.1.0] - 2022-05-23

4.0.0 - 2021-09-30

This release brings new functionality and many fixes.

  1. Key changes
  2. Note for users of v4.0.0-rc.12
  3. Breaking changes
  4. New
  5. Fixed

Key changes

<summary>   Diff of changed API (click to expand)</summary>
+  Collection.[Symbol.iterator]
+  Collection.toJSON
+  Collection.update
+  Collection.Indexed.[Symbol.iterator]
+  Collection.Indexed.toJSON
+  Collection.Indexed.update
+  Collection.Indexed.zipAll
+  Collection.Keyed.[Symbol.iterator]
+  Collection.Keyed.toJSON
+  Collection.Keyed.update
+  Collection.Set.[Symbol.iterator]
+  Collection.Set.toJSON
+  Collection.Set.update
-  Collection.size
-  Collection.Indexed.size
-  Collection.Keyed.size
-  Collection.Set.size

+  List.[Symbol.iterator]
+  List.toJSON
+  List.wasAltered
+  List.zipAll
-  List.mergeDeep
-  List.mergeDeepWith
-  List.mergeWith

+  Map.[Symbol.iterator]
+  Map.deleteAll
+  Map.toJSON
+  Map.wasAltered

+  OrderedMap.[Symbol.iterator]
+  OrderedMap.deleteAll
+  OrderedMap.toJSON
+  OrderedMap.wasAltered
+  OrderedSet.[Symbol.iterator]
+  OrderedSet.toJSON
+  OrderedSet.update
+  OrderedSet.wasAltered
+  OrderedSet.zip
+  OrderedSet.zipAll
+  OrderedSet.zipWith

+  Record.[Symbol.iterator]
+  Record.asImmutable
+  Record.asMutable
+  Record.clear
+  Record.delete
+  Record.deleteIn
+  Record.merge
+  Record.mergeDeep
+  Record.mergeDeepIn
+  Record.mergeDeepWith
+  Record.mergeIn
+  Record.mergeWith
+  Record.set
+  Record.setIn
+  Record.toJSON
+  Record.update
+  Record.updateIn
+  Record.wasAltered
+  Record.withMutations
+  Record.Factory.displayName
-  Record.butLast
-  Record.concat
-  Record.count
-  Record.countBy
-  Record.entries
-  Record.entrySeq
-  Record.every
-  Record.filter
-  Record.filterNot
-  Record.find
-  Record.findEntry
-  Record.findKey
-  Record.findLast
-  Record.findLastEntry
-  Record.findLastKey
-  Record.first
-  Record.flatMap
-  Record.flatten
-  Record.flip
-  Record.forEach
-  Record.groupBy
-  Record.includes
-  Record.isEmpty
-  Record.isSubset
-  Record.isSuperset
-  Record.join
-  Record.keyOf
-  Record.keySeq
-  Record.keys
-  Record.last
-  Record.lastKeyOf
-  Record.map
-  Record.mapEntries
-  Record.mapKeys
-  Record.max
-  Record.maxBy
-  Record.min
-  Record.minBy
-  Record.reduce
-  Record.reduceRight
-  Record.rest
-  Record.reverse
-  Record.skip
-  Record.skipLast
-  Record.skipUntil
-  Record.skipWhile
-  Record.slice
-  Record.some
-  Record.sort
-  Record.sortBy
-  Record.take
-  Record.takeLast
-  Record.takeUntil
-  Record.takeWhile
-  Record.toArray
-  Record.toIndexedSeq
-  Record.toKeyedSeq
-  Record.toList
-  Record.toMap
-  Record.toOrderedMap
-  Record.toOrderedSet
-  Record.toSet
-  Record.toSetSeq
-  Record.toStack
-  Record.valueSeq
-  Record.values

+  Seq.[Symbol.iterator]
+  Seq.toJSON
+  Seq.update
+  Seq.Indexed.[Symbol.iterator]
+  Seq.Indexed.toJSON
+  Seq.Indexed.update
+  Seq.Indexed.zipAll
+  Seq.Keyed.[Symbol.iterator]
+  Seq.Keyed.toJSON
+  Seq.Keyed.update
+  Seq.Set.[Symbol.iterator]
+  Seq.Set.toJSON
+  Seq.Set.update

+  Set.[Symbol.iterator]
+  Set.toJSON
+  Set.update
+  Set.wasAltered

+  Stack.[Symbol.iterator]
+  Stack.toJSON
+  Stack.update
+  Stack.wasAltered
+  Stack.zipAll

+  ValueObject.equals
+  ValueObject.hashCode

-  Iterable.*
-  Iterable.Indexed.*
-  Iterable.Keyed.*
-  Iterable.Set.*

Note for users of v4.0.0-rc.12

There were mostly bugfixes and improvements since RC 12. Upgrading should be painless for most users. However, there is one breaking change: The behavior of merge and mergeDeep has changed. See below for details.

BREAKING

merge()

  • No longer use value-equality within merge() (#1391)

    This rectifies an inconsistent behavior between x.merge(y) and x.mergeDeep(y) where merge would use === on leaf values to determine return-self optimizations, while mergeDeep would use is(). This improves consistency across the library and avoids a possible performance pitfall.

  • No longer deeply coerce argument to merge() (#1339)

    Previously, the argument provided to merge() was deeply converted to Immutable collections via fromJS(). This was the only function in the library which calls fromJS() indirectly, and it was surprising and made it difficult to understand what the result of merge() would be. Now, the value provided to merge() is only shallowly converted to an Immutable collection, similar to related methods in the library. This may change the behavior of your calls to merge().

mergeDeep()

  • Replace incompatible collections when merging nested data (#1840)

    It will no longer merge lists of tuples into maps. For more information see #1840 and the updated mergeDeep() documentation.

  • Concat Lists when merging deeply (#1344)

    Previously, calling map.mergeDeep() with a value containing a List would replace the values in the original List. This has always been confusing, and does not properly treat List as a monoid. Now, List.merge is simply an alias for List.concat, and map.mergeDeep() will concatenate deeply-found lists instead of replacing them.

Seq

  • Remove IteratorSequence. Do not attempt to detect iterators in Seq(). (#1589)

    Iterables can still be provided to Seq(), and most Iterators are also Iterables, so this change should not affect the vast majority of uses. For more information, see PR #1589

  • Remove Seq.of() (#1311, #1310)

    This method has been removed since it cannot be correctly typed. It's recommended to convert Seq.of(1, 2, 3) to Seq([1, 2, 3]).

isImmutable()

  • isImmutable() now returns true for collections currently within a withMutations() call. (#1374)

    Previously, isImmutable() did double-duty of both determining if a value was a Collection or Record from this library as well as if it was outside a withMutations() call. This latter case caused confusion and was rarely used.

toArray()

  • KeyedCollection.toArray() returns array of tuples. (#1340)

    Previously, calling toArray() on a keyed collection (incl Map and OrderedMap) would discard keys and return an Array of values. This has always been confusing, and differs from Array.from(). Now, calling toArray() on a keyed collection will return an Array of [key, value] tuples, matching the behavior of Array.from().

concat()

  • list.concat() now has a slightly more efficient implementation and map.concat() is an alias for map.merge(). (#1373)

    In rare cases, this may affect use of map.concat() which expected slightly different behavior from map.merge().

Collection, formerly Iterable

  • The Iterable class has been renamed to Collection, and isIterable() has been renamed to isCollection(). Aliases with the existing names exist to make transitioning code easier.

Record

  • Record is no longer an Immutable Collection type.
    • Now isCollection(myRecord) returns false instead of true.
    • The sequence API (such as map, filter, forEach) no longer exist on Records.
    • delete() and clear() no longer exist on Records.

Other breaking changes

  • Potentially Breaking: Improve hash speed and avoid collision for common values (#1629)

    Causes some hash values to change, which could impact the order of iteration of values in some Maps (which are already advertised as unordered, but highlighting just to be safe)

  • Node buffers no longer considered value-equal (#1437)

  • Plain Objects and Arrays are no longer considered opaque values (#1369)

    This changes the behavior of a few common methods with respect to plain Objects and Arrays where these were previously considered opaque to merge() and setIn(), they now are treated as collections and can be merged into and updated (persistently). This offers an exciting alternative to small Lists and Records.

  • The "predicate" functions, isCollection, isKeyed, isIndexed, isAssociative have been moved from Iterable. to the top level exports.

  • The toJSON() method performs a shallow conversion (previously it was an alias for toJS(), which remains a deep conversion).

  • Some minor implementation details have changed, which may require updates to libraries which deeply integrate with Immutable.js's private APIs.

  • The Cursor API is officially deprecated. Use immutable-cursor instead.

  • Potentially Breaking: [TypeScript] Remove Iterable<T> as tuple from Map constructor types (#1626)

    Typescript allowed constructing a Map with a list of List instances, assuming each was a key, value pair. While this runtime behavior still works, this type led to more issues than it solved, so it has been removed. (Note, this may break previous v4 rcs, but is not a change against v3)

New

  • Update TypeScript and Flow definitions:
    • The Flowtype and TypeScript type definitions have been completely rewritten with much higher quality and accuracy, taking advantage of the latest features from both tools.
    • Simplified TypeScript definition files to support all UMD use cases (#1854)
    • Support Typescript 3 (#1593)
    • Support Typescript strictNullChecks (#1168)
    • Flow types to be compatible with the latest version 0.160.0
    • Enable flow strict (#1580)
  • Add "sideEffects: false" to package.json (#1661)

  • Use ES standard for iterator method reuse (#1867)

  • Generalize fromJS() and Seq() to support Sets (#1865)

  • Top level predicate functions (#1600)

    New functions are exported from the immutable module: isSeq(), isList(), isMap(), isOrderedMap(), isStack(), isSet(), isOrderedSet(), and isRecord().

  • Improve performance of toJS (#1581)

    Cursory test is >10% faster than both v3.8.2 and v4.0.0-rc.7, and corrects the regression since v4.0.0-rc.9.

  • Added optional notSetValue in first() and last() (#1556)

  • Make isArrayLike check more precise to avoid false positives (#1520)

  • map() for List, Map, and Set returns itself for no-ops (#1455) (5726bd1)

  • Hash functions as objects, allowing functions as values in collections (#1485)

  • Functional API for get(), set(), and more which support both Immutable.js collections and plain Objects and Arrays (#1369)

  • Relicensed as MIT (#1320)

  • Support for Transducers! (ee9c68f1)

  • Add new method, zipAll() (#1195)

  • Bundle and distribute an "es module" so Webpack and Rollup can use tree-shaking for smaller builds (#1204)

  • Warn instead of throw when getIn() has a bad path (668f2236)

  • A new predicate function isValueObject() helps to detect objects which implement equals() and hashCode(), and type definitions now define the interface ValueObject which you can implement in your own code to create objects which behave as values and can be keys in Maps or entries in Sets.

  • Using fromJS() with a "reviver" function now provides access to the key path to each translated value. (#1118)

Fixed

  • Fix issue with IE11 and missing Symbol.iterator (#1850)

  • Fix ordered set with map (#1663)

  • Do not modify iter during List.map and Map.map (#1649)

  • Fix ordered map delete all (#1777)

  • Hash symbols as objects (#1753)

  • Fix returning a Record in merge() when Record is empty (#1785)

  • Fix for RC~12: Records from different factories aren't equal (#1734)

  • "too much recursion" error when creating a Record type from an instance of another Record (#1690)

  • Fix glob for npm format script on Windows (#18)

  • Remove deprecated cursor API (#13)

  • Add missing es exports (#1740)

  • Support nulls in genTypeDefData.js (#185)

  • Support isPlainObj in IE11 and other esoteric parameters f3a6d5ce

  • Set.map produces valid underlying map (#1606)

  • Support isPlainObj with constructor key (#1627)

  • groupBy no longer returns a mutable Map instance (#1602)

  • Fix issue where refs can recursively collide, corrupting .size (#1598)

  • Throw error in mergeWith() method if missing the required merger function (#1543)

  • Update isPlainObj() to workaround Safari bug and allow cross-realm values (#1557)

  • Fix missing "& T" to some methods in RecordInstance (#1464)

  • Make notSetValue optional for typed Records (#1461) (a1029bb)

  • Export type of RecordInstance (#1434)

  • Fix Record size check in merge() (#1521)

  • Fix Map#concat being not defined (#1402)

  • getIn() no longer throws when encountering a missing path (#1361)
  • Do not throw when printing value that cannot be coerced to primitive (#1334)
  • Do not throw from hasIn (#1319)

  • Long hash codes no longer cause an infinite loop (#1175)

  • slice() which should return an empty set could return a full set or vice versa (#1245, #1287)

  • Ensure empty slices do not throw when iterated (#1220)

  • Error during equals check on Record with undefined or null (#1208)

  • Fix size of count() after filtering or flattening (#1171)

3.8.2 - 2017-10-05

Released in 2017, still the most commonly used release.