Mutate a copy of data without changing the original source
npx @tessl/cli install tessl/npm-immutability-helper@3.1.00
# Immutability Helper
1
2
Immutability Helper provides a MongoDB-inspired syntax for creating modified copies of JavaScript data structures without mutating the original data. It serves as a drop-in replacement for React's deprecated `react-addons-update`, offering powerful immutable update operations with shallow copying for performance.
3
4
## Package Information
5
6
- **Package Name**: immutability-helper
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install immutability-helper`
10
11
## Core Imports
12
13
```typescript
14
import update from "immutability-helper";
15
import { Context, extend, isEquals, invariant } from "immutability-helper";
16
```
17
18
For CommonJS:
19
20
```javascript
21
const update = require("immutability-helper");
22
const { Context, extend, isEquals, invariant } = require("immutability-helper");
23
```
24
25
## Basic Usage
26
27
```typescript
28
import update from "immutability-helper";
29
30
// Update arrays
31
const initialArray = [1, 2, 3];
32
const newArray = update(initialArray, { $push: [4] }); // [1, 2, 3, 4]
33
34
// Update objects
35
const obj = { a: 5, b: 3 };
36
const newObj = update(obj, { b: { $set: 6 } }); // { a: 5, b: 6 }
37
38
// Nested updates
39
const collection = [1, 2, { a: [12, 17, 15] }];
40
const newCollection = update(collection, {
41
2: { a: { $splice: [[1, 1, 13, 14]] } }
42
}); // [1, 2, { a: [12, 13, 14, 15] }]
43
44
// Function-based updates
45
const updated = update(obj, { b: { $apply: x => x * 2 } });
46
```
47
48
## Architecture
49
50
Immutability Helper is built around several core concepts:
51
52
- **Update Function**: The main `update(object, spec)` function that applies specifications to data
53
- **Command System**: MongoDB-inspired `$`-prefixed commands for different operation types
54
- **Context System**: `Context` class for isolated environments with custom commands
55
- **Type Safety**: Full TypeScript support with generic type preservation
56
- **Shallow Copying**: Efficient copying strategy that only copies changed branches
57
- **Reference Equality**: Preserves object references when no changes occur for performance
58
59
## Capabilities
60
61
### Array Operations
62
63
Core operations for modifying arrays including push, unshift, and splice operations.
64
65
```typescript { .api }
66
// Push items to end of array
67
{ $push: ReadonlyArray<T> }
68
69
// Add items to beginning of array
70
{ $unshift: ReadonlyArray<T> }
71
72
// Perform splice operations
73
{ $splice: ReadonlyArray<[number, number?] | [number, number, ...T[]]> }
74
```
75
76
[Array Operations](./array-operations.md)
77
78
### Object Operations
79
80
Operations for modifying object properties including set, merge, toggle, and unset.
81
82
```typescript { .api }
83
// Replace target entirely
84
{ $set: T }
85
86
// Shallow merge properties
87
{ $merge: Partial<T> }
88
89
// Toggle boolean fields
90
{ $toggle: ReadonlyArray<keyof T> }
91
92
// Remove properties
93
{ $unset: ReadonlyArray<keyof T> }
94
```
95
96
[Object Operations](./object-operations.md)
97
98
### Map and Set Operations
99
100
Specialized operations for ES6 Map and Set data structures.
101
102
```typescript { .api }
103
// Add entries to Map or Set
104
{ $add: ReadonlyArray<[K, V]> | ReadonlyArray<T> }
105
106
// Remove entries from Map or Set
107
{ $remove: ReadonlyArray<K> }
108
```
109
110
[Map and Set Operations](./map-set-operations.md)
111
112
### Function Operations
113
114
Function-based operations for complex transformations and custom logic.
115
116
```typescript { .api }
117
// Apply function to current value
118
{ $apply: (v: T) => T }
119
120
// Shorthand: pass function directly
121
(v: T) => T
122
```
123
124
[Function Operations](./function-operations.md)
125
126
### Context and Extension System
127
128
Advanced features for creating isolated contexts and adding custom commands.
129
130
```typescript { .api }
131
class Context {
132
constructor();
133
extend<T>(directive: string, fn: (param: any, old: T) => T): void;
134
update<T, C extends CustomCommands<object> = never>(object: T, $spec: Spec<T, C>): T;
135
get isEquals(): (x: any, y: any) => boolean;
136
set isEquals(value: (x: any, y: any) => boolean);
137
}
138
139
function extend<T>(directive: string, fn: (param: any, old: T) => T): void;
140
```
141
142
[Extension System](./extension-system.md)
143
144
## Limitations
145
146
**Important:** The `update` function only works for _data properties_, not for _accessor properties_ defined with `Object.defineProperty`. It does not see accessor properties and may create shadowing data properties which could break application logic depending on setter side effects. Therefore `update` should only be used on plain data objects that contain only _data properties_ as descendants.
147
148
## Core Types
149
150
```typescript { .api }
151
// Main update function type
152
function update<T, C extends CustomCommands<object> = never>(
153
object: T,
154
$spec: Spec<T, C>
155
): T;
156
157
// Specification type for updates
158
type Spec<T, C extends CustomCommands<object> = never> =
159
| (T extends (Array<infer U> | ReadonlyArray<infer U>) ? ArraySpec<U, C> :
160
T extends (Map<infer K, infer V> | ReadonlyMap<infer K, infer V>) ? MapSpec<K, V, C> :
161
T extends (Set<infer X> | ReadonlySet<infer X>) ? SetSpec<X> :
162
T extends object ? ObjectSpec<T, C> :
163
never)
164
| { $set: T }
165
| { $apply: (v: T) => T }
166
| ((v: T) => T)
167
| (C extends CustomCommands<infer O> ? O : never);
168
169
// Custom commands type brand
170
type CustomCommands<T> = T & { __noInferenceCustomCommandsBrand: any };
171
172
// Specialized spec types
173
type ArraySpec<T, C extends CustomCommands<object>> =
174
| { $push: ReadonlyArray<T> }
175
| { $unshift: ReadonlyArray<T> }
176
| { $splice: ReadonlyArray<[number, number?] | [number, number, ...T[]]> }
177
| { [index: string]: Spec<T, C> };
178
179
type MapSpec<K, V, C extends CustomCommands<object>> =
180
| { $add: ReadonlyArray<[K, V]> }
181
| { $remove: ReadonlyArray<K> }
182
| { [key: string]: Spec<V, C> };
183
184
type SetSpec<T> =
185
| { $add: ReadonlyArray<T> }
186
| { $remove: ReadonlyArray<T> };
187
188
type ObjectSpec<T, C extends CustomCommands<object>> =
189
| { $toggle: ReadonlyArray<keyof T> }
190
| { $unset: ReadonlyArray<keyof T> }
191
| { $merge: Partial<T> }
192
| { [K in keyof T]?: Spec<T[K], C> };
193
194
// Utility functions
195
/**
196
* Throws an error if condition is false
197
* @param condition - Boolean condition to check
198
* @param message - Function that returns error message
199
*/
200
function invariant(condition: boolean, message: () => string): void;
201
202
/**
203
* Default equality function used for change detection
204
* Can be overridden via Context.isEquals
205
*/
206
const isEquals: (x: any, y: any) => boolean;
207
```