or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration-utilities.mdcore-production.mddraft-management.mdindex.mdpatches-system.md
tile.json

tessl/npm-immer

Create immutable state by mutating the current one with structural sharing

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/immer@10.0.x

To install, run

npx @tessl/cli install tessl/npm-immer@10.0.0

index.mddocs/

Immer

Immer is a comprehensive immutable state management library that allows developers to create the next immutable state by simply mutating the current state tree. It provides a powerful produce function that works with structural sharing to efficiently create new immutable data structures while maintaining the familiar mutative JavaScript syntax.

Package Information

  • Package Name: immer
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install immer

Core Imports

import { produce, Draft, Immutable, enablePatches } from "immer";

For CommonJS:

const { produce, enablePatches } = require("immer");

Basic Usage

import { produce } from "immer";

// Basic state mutation
const baseState = {
  todos: [
    { id: 1, text: "Learn Immer", done: false },
    { id: 2, text: "Use Immer in project", done: false }
  ],
  user: { name: "John", age: 30 }
};

// Create new state by "mutating" a draft
const nextState = produce(baseState, draft => {
  draft.todos[0].done = true;
  draft.user.age = 31;
  draft.todos.push({ id: 3, text: "Master Immer", done: false });
});

// baseState is unchanged, nextState contains the updates
console.log(baseState === nextState); // false
console.log(baseState.todos === nextState.todos); // false (structural sharing)

Architecture

Immer is built around several key components:

  • Proxy System: Creates draft proxies that track mutations without affecting the original data
  • Structural Sharing: Efficiently reuses unchanged parts of the state tree in the new immutable state
  • Producer Functions: Recipe functions that describe how to transform state using familiar mutation syntax
  • Type Safety: Full TypeScript support with Draft<T> and Immutable<T> utility types
  • Plugin Architecture: Optional plugins for patches tracking and Map/Set support
  • Zero Dependencies: Self-contained with no external runtime dependencies

Capabilities

Core Production

The main produce function and related production utilities for creating immutable state through mutation-like syntax.

function produce<T>(
  base: T,
  recipe: (draft: Draft<T>) => void | T | undefined
): T;

function produce<T>(
  recipe: (draft: Draft<T>) => void | T | undefined
): (base: T) => T;

function produceWithPatches<T>(
  base: T,
  recipe: (draft: Draft<T>) => void | T | undefined
): [T, Patch[], Patch[]];

Core Production

Draft Management

Direct draft creation and manipulation utilities for advanced use cases and fine-grained control over the immutable update process.

function createDraft<T extends Objectish>(base: T): Draft<T>;

function finishDraft<D extends Draft<any>>(
  draft: D,
  patchListener?: PatchListener
): D extends Draft<infer T> ? T : never;

function current<T>(value: T): T;

function original<T>(value: T): T | undefined;

Draft Management

Patches System

Advanced patch tracking system for implementing undo/redo, debugging, and state synchronization features.

function applyPatches<T>(base: T, patches: readonly Patch[]): T;

function enablePatches(): void;

interface Patch {
  op: "replace" | "remove" | "add";
  path: (string | number)[];
  value?: any;
}

Patches System

Configuration & Utilities

Configuration options and utility functions for customizing Immer behavior and working with types.

function setAutoFreeze(value: boolean): void;

function setUseStrictShallowCopy(value: boolean | "class_only"): void;

function freeze<T>(obj: T, deep?: boolean): T;

function isDraft(value: any): boolean;

function isDraftable(value: any): boolean;

function castDraft<T>(value: T): Draft<T>;

function castImmutable<T>(value: T): Immutable<T>;

Configuration & Utilities

Map & Set Support

Plugin that enables Immer to work with Map and Set objects, providing draft versions that track mutations.

function enableMapSet(): void;

Enables drafting of Map and Set instances with full mutation tracking and structural sharing support.

Usage Examples:

import { produce, enableMapSet } from "immer";

// Enable Map/Set support
enableMapSet();

// Working with Maps
const stateWithMap = {
  userMap: new Map([
    ["user1", { name: "Alice", age: 30 }],
    ["user2", { name: "Bob", age: 25 }]
  ])
};

const updatedState = produce(stateWithMap, draft => {
  // Map methods work normally in drafts
  draft.userMap.set("user3", { name: "Charlie", age: 35 });
  draft.userMap.get("user1")!.age = 31;
  draft.userMap.delete("user2");
});

// Working with Sets
const stateWithSet = {
  tags: new Set(["javascript", "react", "typescript"])
};

const updatedSet = produce(stateWithSet, draft => {
  draft.tags.add("immer");
  draft.tags.delete("react");
});

Core Types

type Draft<T> = T extends PrimitiveType
  ? T
  : T extends AtomicObject
  ? T
  : T extends ReadonlyMap<infer K, infer V>
  ? Map<Draft<K>, Draft<V>>
  : T extends ReadonlySet<infer V>
  ? Set<Draft<V>>
  : T extends WeakReferences
  ? T
  : T extends object
  ? WritableDraft<T>
  : T;

type PrimitiveType = number | string | boolean;
type AtomicObject = Function | Promise<any> | Date | RegExp;

type Immutable<T> = T extends PrimitiveType
  ? T
  : T extends AtomicObject
  ? T
  : T extends ReadonlyMap<infer K, infer V>
  ? ReadonlyMap<Immutable<K>, Immutable<V>>
  : T extends ReadonlySet<infer V>
  ? ReadonlySet<Immutable<V>>
  : T extends WeakReferences
  ? T
  : T extends object
  ? { readonly [K in keyof T]: Immutable<T[K]> }
  : T;

type WritableDraft<T> = {
  -readonly [K in keyof T]: Draft<T[K]>;
};

type Producer<T> = (draft: Draft<T>) => T | void | undefined | (T extends undefined ? typeof NOTHING : never);

type PatchListener = (patches: Patch[], inversePatches: Patch[]) => void;

type Objectish = object;

Constants

const nothing: unique symbol;
const immerable: unique symbol;

The nothing symbol is used in producer functions to indicate that a property should be deleted or set to undefined. When returned from a producer, it causes the entire state to become undefined.

The immerable symbol can be added to class prototypes to make them draftable by Immer. Classes marked with this symbol will be treated as plain objects during drafting.

Immer Class

class Immer {
  constructor(config?: {
    autoFreeze?: boolean;
    useStrictShallowCopy?: boolean | "class_only";
  });
  
  produce: IProduce;
  produceWithPatches: IProduceWithPatches;
  createDraft<T extends Objectish>(base: T): Draft<T>;
  finishDraft<D extends Draft<any>>(
    draft: D,
    patchListener?: PatchListener
  ): D extends Draft<infer T> ? T : never;
  setAutoFreeze(value: boolean): void;
  setUseStrictShallowCopy(value: boolean | "class_only"): void;
  applyPatches<T extends Objectish>(base: T, patches: readonly Patch[]): T;
}

type StrictMode = boolean | "class_only";
type Objectish = object;

type ValidRecipeReturnType<State> =
  | State
  | void
  | undefined
  | (State extends undefined ? typeof NOTHING : never);

type WeakReferences = WeakMap<any, any> | WeakSet<any>;

Create custom Immer instances with specific configurations for different use cases.

Advanced Usage

import { produce, enablePatches, applyPatches, Immer } from "immer";

// Enable patches plugin for tracking changes
enablePatches();

// Use produceWithPatches to get change information
const [nextState, patches, inversePatches] = produceWithPatches(baseState, draft => {
  draft.todos[0].done = true;
});

// Apply patches to recreate the same state change
const recreatedState = applyPatches(baseState, patches);

// Create isolated Immer instance with custom configuration
const immer = new Immer({
  autoFreeze: false,
  useStrictShallowCopy: "class_only"
});

const result = immer.produce(baseState, draft => {
  draft.user.name = "Jane";
});