or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

array-operations.mdextension-system.mdfunction-operations.mdindex.mdmap-set-operations.mdobject-operations.md
tile.json

tessl/npm-immutability-helper

Mutate a copy of data without changing the original source

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/immutability-helper@3.1.x

To install, run

npx @tessl/cli install tessl/npm-immutability-helper@3.1.0

index.mddocs/

Immutability Helper

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.

Package Information

  • Package Name: immutability-helper
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install immutability-helper

Core Imports

import update from "immutability-helper";
import { Context, extend, isEquals, invariant } from "immutability-helper";

For CommonJS:

const update = require("immutability-helper");
const { Context, extend, isEquals, invariant } = require("immutability-helper");

Basic Usage

import update from "immutability-helper";

// Update arrays
const initialArray = [1, 2, 3];
const newArray = update(initialArray, { $push: [4] }); // [1, 2, 3, 4]

// Update objects
const obj = { a: 5, b: 3 };
const newObj = update(obj, { b: { $set: 6 } }); // { a: 5, b: 6 }

// Nested updates
const collection = [1, 2, { a: [12, 17, 15] }];
const newCollection = update(collection, {
  2: { a: { $splice: [[1, 1, 13, 14]] } }
}); // [1, 2, { a: [12, 13, 14, 15] }]

// Function-based updates
const updated = update(obj, { b: { $apply: x => x * 2 } });

Architecture

Immutability Helper is built around several core concepts:

  • Update Function: The main update(object, spec) function that applies specifications to data
  • Command System: MongoDB-inspired $-prefixed commands for different operation types
  • Context System: Context class for isolated environments with custom commands
  • Type Safety: Full TypeScript support with generic type preservation
  • Shallow Copying: Efficient copying strategy that only copies changed branches
  • Reference Equality: Preserves object references when no changes occur for performance

Capabilities

Array Operations

Core operations for modifying arrays including push, unshift, and splice operations.

// Push items to end of array
{ $push: ReadonlyArray<T> }

// Add items to beginning of array  
{ $unshift: ReadonlyArray<T> }

// Perform splice operations
{ $splice: ReadonlyArray<[number, number?] | [number, number, ...T[]]> }

Array Operations

Object Operations

Operations for modifying object properties including set, merge, toggle, and unset.

// Replace target entirely
{ $set: T }

// Shallow merge properties
{ $merge: Partial<T> }

// Toggle boolean fields
{ $toggle: ReadonlyArray<keyof T> }

// Remove properties
{ $unset: ReadonlyArray<keyof T> }

Object Operations

Map and Set Operations

Specialized operations for ES6 Map and Set data structures.

// Add entries to Map or Set
{ $add: ReadonlyArray<[K, V]> | ReadonlyArray<T> }

// Remove entries from Map or Set
{ $remove: ReadonlyArray<K> }

Map and Set Operations

Function Operations

Function-based operations for complex transformations and custom logic.

// Apply function to current value
{ $apply: (v: T) => T }

// Shorthand: pass function directly
(v: T) => T

Function Operations

Context and Extension System

Advanced features for creating isolated contexts and adding custom commands.

class Context {
  constructor();
  extend<T>(directive: string, fn: (param: any, old: T) => T): void;
  update<T, C extends CustomCommands<object> = never>(object: T, $spec: Spec<T, C>): T;
  get isEquals(): (x: any, y: any) => boolean;
  set isEquals(value: (x: any, y: any) => boolean);
}

function extend<T>(directive: string, fn: (param: any, old: T) => T): void;

Extension System

Limitations

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.

Core Types

// Main update function type
function update<T, C extends CustomCommands<object> = never>(
  object: T, 
  $spec: Spec<T, C>
): T;

// Specification type for updates
type Spec<T, C extends CustomCommands<object> = never> = 
  | (T extends (Array<infer U> | ReadonlyArray<infer U>) ? ArraySpec<U, C> :
     T extends (Map<infer K, infer V> | ReadonlyMap<infer K, infer V>) ? MapSpec<K, V, C> :
     T extends (Set<infer X> | ReadonlySet<infer X>) ? SetSpec<X> :
     T extends object ? ObjectSpec<T, C> :
     never)
  | { $set: T }
  | { $apply: (v: T) => T }
  | ((v: T) => T)
  | (C extends CustomCommands<infer O> ? O : never);

// Custom commands type brand
type CustomCommands<T> = T & { __noInferenceCustomCommandsBrand: any };

// Specialized spec types
type ArraySpec<T, C extends CustomCommands<object>> =
  | { $push: ReadonlyArray<T> }
  | { $unshift: ReadonlyArray<T> }
  | { $splice: ReadonlyArray<[number, number?] | [number, number, ...T[]]> }
  | { [index: string]: Spec<T, C> };

type MapSpec<K, V, C extends CustomCommands<object>> =
  | { $add: ReadonlyArray<[K, V]> }
  | { $remove: ReadonlyArray<K> }
  | { [key: string]: Spec<V, C> };

type SetSpec<T> =
  | { $add: ReadonlyArray<T> }
  | { $remove: ReadonlyArray<T> };

type ObjectSpec<T, C extends CustomCommands<object>> =
  | { $toggle: ReadonlyArray<keyof T> }
  | { $unset: ReadonlyArray<keyof T> }
  | { $merge: Partial<T> }
  | { [K in keyof T]?: Spec<T[K], C> };

// Utility functions
/**
 * Throws an error if condition is false
 * @param condition - Boolean condition to check
 * @param message - Function that returns error message
 */
function invariant(condition: boolean, message: () => string): void;

/**
 * Default equality function used for change detection
 * Can be overridden via Context.isEquals
 */
const isEquals: (x: any, y: any) => boolean;