0
# Core Constructor and Utilities
1
2
Core functionality for creating immutable data structures and utility methods for type checking and configuration.
3
4
## Capabilities
5
6
### Immutable Constructor
7
8
Creates an immutable version of the provided data structure with backwards compatibility.
9
10
```javascript { .api }
11
/**
12
* Creates an immutable version of the provided data structure
13
* @param {*} obj - Data to make immutable (Array, Object, Date, or primitive)
14
* @param {Object} [options] - Configuration options
15
* @param {Object} [options.prototype] - Custom prototype for objects
16
* @param {number} [stackRemaining] - Stack depth limit for circular reference protection (default: 64, development only)
17
* @returns {*} Immutable version of the input
18
*/
19
function Immutable(obj, options, stackRemaining);
20
```
21
22
**Usage Examples:**
23
24
```javascript
25
const Immutable = require("seamless-immutable");
26
27
// Arrays become ImmutableArrays
28
const immutableArray = Immutable([1, 2, 3]);
29
console.log(Array.isArray(immutableArray)); // true - still an array!
30
31
// Objects become ImmutableObjects
32
const immutableObj = Immutable({name: "Alice", age: 30});
33
console.log(typeof immutableObj); // "object"
34
35
// Dates become ImmutableDates
36
const immutableDate = Immutable(new Date());
37
38
// Primitives are returned as-is (already immutable)
39
const str = Immutable("hello"); // Returns "hello"
40
const num = Immutable(42); // Returns 42
41
42
// Custom prototype example
43
function Person(name) {
44
this.name = name;
45
}
46
Person.prototype.greet = function() {
47
return `Hello, I'm ${this.name}`;
48
};
49
50
const person = new Person("Bob");
51
const immutablePerson = Immutable(person, {prototype: Person.prototype});
52
console.log(immutablePerson.greet()); // "Hello, I'm Bob"
53
54
// Stack depth protection (development build only)
55
const deepObject = {};
56
let current = deepObject;
57
for (let i = 0; i < 100; i++) {
58
current.nested = {};
59
current = current.nested;
60
}
61
// Immutable(deepObject); // Would throw ImmutableError after default 64 levels
62
63
// Increase stack limit for legitimately deep (but not circular) objects
64
const allowDeeper = Immutable(deepObject, null, 256); // Allow 256 levels
65
```
66
67
### Immutable.from
68
69
Alias for the main Immutable constructor, provided for linter compatibility.
70
71
```javascript { .api }
72
/**
73
* Alias for Immutable() for linter compatibility (e.g., ESLint new-cap rule)
74
* @param {*} obj - Data to make immutable
75
* @param {Object} [options] - Configuration options
76
* @returns {*} Immutable version of the input
77
*/
78
Immutable.from = Immutable;
79
```
80
81
**Usage Example:**
82
83
```javascript
84
// These are functionally identical
85
const arr1 = Immutable([1, 2, 3]);
86
const arr2 = Immutable.from([1, 2, 3]);
87
```
88
89
### isImmutable
90
91
Checks whether a value is immutable according to seamless-immutable.
92
93
```javascript { .api }
94
/**
95
* Checks if a value is immutable
96
* @param {*} target - Value to check for immutability
97
* @returns {boolean} True if the value is immutable
98
*/
99
function isImmutable(target);
100
```
101
102
**Usage Examples:**
103
104
```javascript
105
const Immutable = require("seamless-immutable");
106
107
const mutableArray = [1, 2, 3];
108
const immutableArray = Immutable([1, 2, 3]);
109
110
console.log(Immutable.isImmutable(mutableArray)); // false
111
console.log(Immutable.isImmutable(immutableArray)); // true
112
113
// Primitives are considered immutable
114
console.log(Immutable.isImmutable("string")); // true
115
console.log(Immutable.isImmutable(42)); // true
116
console.log(Immutable.isImmutable(null)); // true
117
```
118
119
### ImmutableError
120
121
Error class thrown when attempting to use mutating methods on immutable data structures.
122
123
```javascript { .api }
124
/**
125
* Error thrown when attempting to use mutating methods on immutable structures
126
* @extends Error
127
*/
128
class ImmutableError extends Error {
129
constructor(message: string);
130
}
131
```
132
133
**Usage Example:**
134
135
```javascript
136
const Immutable = require("seamless-immutable");
137
138
const immutableArray = Immutable([1, 2, 3]);
139
140
try {
141
immutableArray.push(4); // This will throw
142
} catch (error) {
143
console.log(error instanceof Immutable.ImmutableError); // true
144
console.log(error.message); // "The push method cannot be invoked on an Immutable data structure."
145
}
146
```
147
148
### Static API
149
150
Alternative API that uses static methods instead of instance methods to avoid method name collisions.
151
152
```javascript { .api }
153
/**
154
* Static API version that avoids adding methods to object instances
155
* All instance methods are available as static methods instead
156
* When using static API, no instance methods are added to immutable objects
157
*/
158
const static;
159
```
160
161
**Usage Examples:**
162
163
```javascript
164
const Immutable = require("seamless-immutable").static;
165
166
const obj = {name: "Alice", age: 30};
167
const immutableObj = Immutable(obj);
168
169
// Instead of: immutableObj.merge({age: 31})
170
const updated = Immutable.merge(immutableObj, {age: 31});
171
172
// Instead of: immutableObj.set("status", "active")
173
const withStatus = Immutable.set(immutableObj, "status", "active");
174
175
// All static methods available:
176
// Immutable.merge, Immutable.set, Immutable.setIn, Immutable.without,
177
// Immutable.update, Immutable.updateIn, Immutable.getIn, Immutable.asMutable,
178
// Immutable.flatMap, Immutable.asObject
179
180
// Note: When using static API, instance methods are NOT available
181
console.log(typeof immutableObj.merge); // "undefined" - no instance methods added
182
```
183
184
**When to Use Static vs Instance API:**
185
186
- **Static API**: Recommended to avoid method name pollution, better for functional programming style, no risk of conflicts with existing object methods
187
- **Instance API**: More concise syntax, familiar object-oriented pattern, default behavior
188
- **Performance**: Both APIs have identical performance characteristics
189
190
### asMutable
191
192
Converts an immutable data structure back to a mutable one.
193
194
```javascript { .api }
195
/**
196
* Convert immutable structure to mutable copy
197
* @param {*} obj - Immutable structure to convert
198
* @param {Object} [options] - Conversion options
199
* @param {boolean} [options.deep] - Recursively convert nested immutable structures
200
* @returns {*} Mutable copy of the structure
201
*/
202
function asMutable(obj, options);
203
204
/**
205
* Convert immutable Date to mutable copy (Date-specific)
206
* @param {Date} date - Immutable Date to convert
207
* @returns {Date} New mutable Date with same time value
208
*/
209
function asMutable(date);
210
```
211
212
**Usage Examples:**
213
214
```javascript
215
const Immutable = require("seamless-immutable");
216
217
const immutableObj = Immutable({
218
name: "Alice",
219
hobbies: ["reading", "coding"],
220
address: {city: "NYC", state: "NY"}
221
});
222
223
// Shallow conversion - nested structures remain immutable
224
const shallowMutable = Immutable.asMutable(immutableObj);
225
shallowMutable.name = "Bob"; // Works
226
// shallowMutable.hobbies.push("gaming"); // Would still throw error
227
228
// Deep conversion - all nested structures become mutable
229
const deepMutable = Immutable.asMutable(immutableObj, {deep: true});
230
deepMutable.name = "Charlie"; // Works
231
deepMutable.hobbies.push("gaming"); // Also works now
232
deepMutable.address.city = "LA"; // Also works now
233
234
console.log(Immutable.isImmutable(shallowMutable)); // false
235
console.log(Immutable.isImmutable(deepMutable)); // false
236
237
// Date-specific asMutable
238
const immutableDate = Immutable(new Date("2023-01-01"));
239
const mutableDate = immutableDate.asMutable();
240
241
// Date methods that would throw on immutable dates now work
242
mutableDate.setFullYear(2024);
243
console.log(mutableDate.getFullYear()); // 2024
244
console.log(immutableDate.getFullYear()); // 2023 - original unchanged
245
```
246
247
## Special Behaviors
248
249
### React Elements
250
React elements are treated as immutable and passed through unchanged to avoid interfering with React's internal structure.
251
252
```javascript
253
const React = require("react");
254
const element = React.createElement("div", {className: "test"});
255
const result = Immutable(element);
256
console.log(element === result); // true - unchanged
257
console.log(Immutable.isImmutable(element)); // true - considered immutable
258
```
259
260
### Promises
261
Promises themselves are not made immutable, but their fulfillment values are automatically made immutable using a transformed promise.
262
263
```javascript
264
const promise = Promise.resolve([1, 2, 3]);
265
const wrappedPromise = Immutable(promise);
266
267
// The wrapped promise will resolve to an immutable array
268
wrappedPromise.then(result => {
269
console.log(Immutable.isImmutable(result)); // true - array is now immutable
270
console.log(Array.isArray(result)); // true - still an array
271
});
272
273
console.log(Immutable.isImmutable(wrappedPromise)); // false - promise itself not immutable
274
```
275
276
### Functions, Errors, Files, Blobs
277
These types are treated as immutable and returned unchanged. This is an intentional abstraction leak for practical reasons.
278
279
```javascript
280
const fn = function() { return "hello"; };
281
const error = new Error("test");
282
const file = new File(["content"], "test.txt");
283
const blob = new Blob(["content"]);
284
285
console.log(Immutable(fn) === fn); // true - unchanged
286
console.log(Immutable(error) === error); // true - unchanged
287
console.log(Immutable(file) === file); // true - unchanged
288
console.log(Immutable(blob) === blob); // true - unchanged
289
290
// All are considered immutable
291
console.log(Immutable.isImmutable(fn)); // true
292
console.log(Immutable.isImmutable(error)); // true
293
console.log(Immutable.isImmutable(file)); // true
294
console.log(Immutable.isImmutable(blob)); // true
295
```
296
297
### Null and Undefined
298
Primitives including `null` and `undefined` are naturally immutable and returned unchanged.
299
300
```javascript
301
console.log(Immutable(null) === null); // true
302
console.log(Immutable(undefined) === undefined); // true
303
console.log(Immutable.isImmutable(null)); // true
304
console.log(Immutable.isImmutable(undefined)); // true
305
```
306
307
### Development vs Production Builds
308
309
The library behaves differently depending on the build environment:
310
311
**Development Build (`process.env.NODE_ENV !== "production"`):**
312
- Objects and arrays are frozen using `Object.freeze()`
313
- Mutating methods throw helpful `ImmutableError` exceptions
314
- Circular reference protection with configurable stack depth (default 64 levels)
315
- Date mutating methods are banned and throw errors
316
317
**Production Build:**
318
- No freezing for better performance (~2x speed improvement)
319
- No defensive method banning (methods still won't work, but fail silently)
320
- No circular reference checking
321
- Smaller bundle size
322
323
```javascript
324
// Development build behavior
325
const arr = Immutable([1, 2, 3]);
326
try {
327
arr.push(4); // Throws ImmutableError with helpful message
328
} catch (e) {
329
console.log(e.message); // "The push method cannot be invoked on an Immutable data structure."
330
}
331
332
// Production build behavior
333
const arr = Immutable([1, 2, 3]);
334
arr.push(4); // Fails silently, arr remains [1, 2, 3]
335
```
336
337
### Circular Reference Protection
338
339
In development builds, the constructor includes protection against circular references with a configurable stack depth limit (default 64 levels).
340
341
```javascript
342
// This would throw an ImmutableError after 64 levels of nesting (development only)
343
const deepObject = {};
344
let current = deepObject;
345
for (let i = 0; i < 100; i++) {
346
current.nested = {};
347
current = current.nested;
348
}
349
// Immutable(deepObject); // Throws error about deeply nested object
350
351
// Increase limit for legitimately deep objects
352
const deeperAllowed = Immutable(deepObject, null, 256);
353
```