Mutability is dangerous, let it go!
Babel plugin that replaces object and array literals with immutable versions… that have super powers!
Elsa is a babel plugin, to make use of it, you need to install it:
npm install --save babel-plugin-elsa
Add elsa as a plugin to your .babelrc
config file (or babel section of package.json
):
{
"plugins": ["babel-plugin-elsa"]
}
Immutability is cool these days. By never altering the contents of an object or array, and instead cloning them when you want to make a change, you avoid common bugs. It's especially popular in React development, but by no means exclusive to React.
There are great libraries out there like Immutable.js that provide a bunch of classes for making immutable objects, but wouldn't it be frosty to turn the default object and arrays into immutable objects?
Don't worry, there are no more puns past this point, they've been put on ice.
Elsa does two things:
- It turns each object literal into a
FrozenObject
. - It changes each array literal into a
FrozenArray
.
Both FrozenObject
s and FrozenArray
s are immutable classes included with Elsa. If you try to change their contents, they'll raise
a TypeError
since they've been frozen with Object.freeze
. Assuming you have strict mode enabled (which you ought to do).
Elsa will:
// Turn this:
const person = {
name: 'Jon',
skills: ['ruby', 'javascript']
};
// into:
const person = new FrozenObject({
name: 'Jon',
skills: new FrozenArray('ruby', 'javascript')
});
Why create these new classes instead of just calling Object.freeze
? To give them super powers, read on!
Elsa turns all array literals (e.g. const coolNumbers = [1,5,-4,20];
) into instances of FrozenArray
.
FrozenArray
inherits from JavaScript's built-in Array. This means it has a .length
, is an instance of Array
, and any of the methods that you'd expect like .forEach
and .map
.
The methods on Array
that would typically mutate the array are reimplemented onFrozenArray
to
instead return a fresh instance of FrozenArray
with changes applied. This includes push
, pop
,
unshift
, shift
, sort
, reverse
, splice
, and fill
.
slice
, map
, and other methods that already return new arrays are still available, but
return FrozenArray
instances instead of a normal mutable array.
pop
and shift
return both the modified arrays and the shifted/popped values as two elements inside an array. That way you can do: [arr, poppedVal] = arr.pop()
.
Examples:
push
:new FrozenArray(1,2,3).push(5)
->[1, 2, 3, 5]
pop
:new FrozenArray(1,2,3).pop()
->[[1, 2], 3]
unshift
:new FrozenArray(1,2,3).shift(0)
->[0,1,2,3]
shift
:new FrozenArray(1,2,3).shift()
->[[2,3], 1]
sort
:new FrozenArray(2,1,3).sort((a,b) => a - b)
->[1,2,3]
Note: Return values are all instances of FrozenArray
Elsa turns all object literals (e.g. const dog = { name: 'Loki', age: 4 };
) into instances of FrozenObject
.
FrozenObject
is identical to a normal JavaScript object, but it has been frozen by Object.freeze
and has a special method called update
, provided by immutability-helper.
To see all the ways you can get a clone, with changes applied, using update
see the immutability-helper docs: https://github.com/kolodny/immutability-helper
One example:
const dog = { name: 'Loki', age: 4, friends: ['Waffles', 'Mochi'] };
const dogClone = dog.update({ name: { $set: 'Loki 2.0' }, friends: { $push: ['Seamus'] } });
// dogClone now contains { name: 'Loki 2.0', age: 4, friends: ['Waffles', 'Mochi', 'Seamus'] }
Elsa will ignore arrays created with new Array(1,2,3)
or Array.of(1,2,3)
, which will produce old
fashioned mutable arrays.
To create mutable objects, you can do new Object({ name: 'Loki' })
, which Elsa will skip over, resulting in old fashioned mutable objects.
- Elsa will only alter object and array literals. If you create an object using a function of
new
constructor, it won't be frozen (at least not by Elsa). Only[…]
and{…}
are frozen by Elsa. - Some libraries expect to receive mutable objects as their parameters. If you receive type errors in
libraries that you use, try sending in an object created with
new Object({…})
. - Elsa only converts your code. Babel, and therefore Elsa, doesn't convert 3rd party modules. This means that any objects produced by libraries won't be frozen.
- You can freeze objects returned by libraries by wrapping it with
new FrozenObject(…)
. - If you don't use Elsa as a Babel plugin, you can still use
FrozenArray
andFrozenObject
with your code. Just doimport FrozenArray from 'babel-plugin-elsa/frozen_array'
and/orimport FrozenObject from 'babel-plugin-elsa/frozen_object'
.
I've written a very basic todo app (of course) that makes use of Elsa.
ISC