or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

draft-management.mddocs/

0

# Draft Management

1

2

Advanced draft creation and manipulation utilities for fine-grained control over the immutable update process. These functions allow you to work directly with drafts outside of the standard `produce` workflow.

3

4

## Capabilities

5

6

### createDraft

7

8

Creates an Immer draft from the given base state, which can be modified until finalized with `finishDraft`.

9

10

```typescript { .api }

11

/**

12

* Create an Immer draft from the given base state

13

* @param base - Must be a plain object, array, or immerable object

14

* @returns A draft that can be modified until finalized

15

*/

16

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

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

import { createDraft, finishDraft } from "immer";

23

24

const baseState = {

25

user: { name: "Alice", age: 25 },

26

todos: ["Task 1", "Task 2"]

27

};

28

29

// Create a draft for manual manipulation

30

const draft = createDraft(baseState);

31

32

// Modify the draft

33

draft.user.age = 26;

34

draft.todos.push("Task 3");

35

draft.user.name = "Alice Smith";

36

37

// Finalize to get the immutable result

38

const nextState = finishDraft(draft);

39

40

console.log(baseState === nextState); // false

41

console.log(baseState.user.age); // 25 (unchanged)

42

console.log(nextState.user.age); // 26 (updated)

43

```

44

45

### finishDraft

46

47

Finalizes an Immer draft from a `createDraft` call, returning the base state (if no changes were made) or a modified copy.

48

49

```typescript { .api }

50

/**

51

* Finalize an Immer draft, returning base state or modified copy

52

* @param draft - Draft from createDraft call (must not be mutated afterwards)

53

* @param patchListener - Optional patch listener for tracking changes

54

* @returns Final immutable state

55

*/

56

function finishDraft<D extends Draft<any>>(

57

draft: D,

58

patchListener?: PatchListener

59

): D extends Draft<infer T> ? T : never;

60

```

61

62

**Usage Examples:**

63

64

```typescript

65

import { createDraft, finishDraft, enablePatches } from "immer";

66

67

// Enable patches for patch listener support

68

enablePatches();

69

70

const state = { items: [1, 2, 3], count: 3 };

71

const draft = createDraft(state);

72

73

// Modify draft

74

draft.items.push(4);

75

draft.count += 1;

76

77

// Finalize with patch tracking

78

const patches: Patch[] = [];

79

const inversePatches: Patch[] = [];

80

81

const result = finishDraft(draft, (p, ip) => {

82

patches.push(...p);

83

inversePatches.push(...ip);

84

});

85

86

console.log(patches);

87

// [

88

// { op: "add", path: ["items", 3], value: 4 },

89

// { op: "replace", path: ["count"], value: 4 }

90

// ]

91

92

// If no changes are made, returns the original object

93

const unchangedDraft = createDraft(state);

94

const unchanged = finishDraft(unchangedDraft);

95

console.log(unchanged === state); // true (no changes, same reference)

96

```

97

98

### current

99

100

Takes a snapshot of the current state of a draft, useful for debugging or when you need to access the current state within a producer function.

101

102

```typescript { .api }

103

/**

104

* Takes a snapshot of the current state of a draft

105

* @param value - Must be an Immer draft

106

* @returns Current state without proxy wrappers (for debugging/inspection)

107

*/

108

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

109

```

110

111

**Usage Examples:**

112

113

```typescript

114

import { produce, current } from "immer";

115

116

const baseState = {

117

user: { name: "Bob", settings: { theme: "light" } },

118

data: [1, 2, 3]

119

};

120

121

const result = produce(baseState, draft => {

122

draft.user.name = "Robert";

123

draft.data.push(4);

124

125

// Get current snapshot for debugging or conditional logic

126

const currentState = current(draft);

127

console.log("Current state:", currentState);

128

// { user: { name: "Robert", settings: { theme: "light" } }, data: [1, 2, 3, 4] }

129

130

// Use current state for conditional updates

131

if (currentState.data.length > 3) {

132

draft.user.settings.theme = "dark";

133

}

134

135

// current() creates a deep copy, safe to mutate for comparisons

136

const snapshot = current(draft);

137

snapshot.temp = "this won't affect the draft";

138

});

139

140

// Useful for logging during complex transformations

141

const complexUpdate = produce(baseState, draft => {

142

// Step 1

143

draft.data.push(5, 6, 7);

144

console.log("After adding items:", current(draft).data);

145

146

// Step 2

147

draft.data = draft.data.filter(x => x % 2 === 0);

148

console.log("After filtering:", current(draft).data);

149

150

// Step 3

151

draft.user.name = draft.user.name.toUpperCase();

152

console.log("Final state:", current(draft));

153

});

154

```

155

156

### original

157

158

Gets the underlying object that is represented by the given draft. Returns `undefined` if the value is not a draft.

159

160

```typescript { .api }

161

/**

162

* Get the underlying object represented by the given draft

163

* @param value - An Immer draft

164

* @returns Original base object or undefined if not a draft

165

*/

166

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

167

```

168

169

**Usage Examples:**

170

171

```typescript

172

import { produce, original } from "immer";

173

174

const baseState = {

175

items: ["apple", "banana"],

176

meta: { created: Date.now() }

177

};

178

179

const result = produce(baseState, draft => {

180

// Get reference to original state

181

const originalState = original(draft);

182

console.log(originalState === baseState); // true

183

184

// Access original values before mutations

185

const originalItems = original(draft.items);

186

console.log(originalItems); // ["apple", "banana"]

187

188

// Make mutations

189

draft.items.push("cherry");

190

draft.meta.updated = Date.now();

191

192

// Original is still unchanged

193

console.log(original(draft.items)); // ["apple", "banana"]

194

console.log(draft.items); // ["apple", "banana", "cherry"] (draft includes changes)

195

196

// Compare with original for conditional logic

197

if (draft.items.length > originalItems!.length) {

198

draft.meta.hasNewItems = true;

199

}

200

});

201

202

// original() returns undefined for non-drafts

203

const regularObject = { name: "test" };

204

console.log(original(regularObject)); // undefined

205

```

206

207

### isDraft

208

209

Returns true if the given value is an Immer draft.

210

211

```typescript { .api }

212

/**

213

* Returns true if the given value is an Immer draft

214

* @param value - Any value to check

215

* @returns Boolean indicating if value is a draft

216

*/

217

function isDraft(value: any): boolean;

218

```

219

220

**Usage Examples:**

221

222

```typescript

223

import { produce, createDraft, isDraft } from "immer";

224

225

const state = { name: "Alice", age: 30 };

226

227

console.log(isDraft(state)); // false

228

229

// Inside produce, parameters are drafts

230

const result = produce(state, draft => {

231

console.log(isDraft(draft)); // true

232

console.log(isDraft(draft.name)); // false (primitives are not drafts)

233

234

draft.nested = { value: 42 };

235

console.log(isDraft(draft.nested)); // true (objects within drafts are drafts)

236

});

237

238

// With createDraft

239

const draft = createDraft(state);

240

console.log(isDraft(draft)); // true

241

242

// Useful for conditional draft handling

243

function processValue(value: any) {

244

if (isDraft(value)) {

245

console.log("Processing draft - mutations will be tracked");

246

value.processed = true;

247

} else {

248

console.log("Processing regular object - mutations will affect original");

249

}

250

}

251

```

252

253

### isDraftable

254

255

Returns true if the given value can be drafted by Immer (i.e., can be converted into a draft).

256

257

```typescript { .api }

258

/**

259

* Returns true if the given value can be drafted by Immer

260

* @param value - Any value to check

261

* @returns Boolean indicating if value can be made into a draft

262

*/

263

function isDraftable(value: any): boolean;

264

```

265

266

**Usage Examples:**

267

268

```typescript

269

import { isDraftable } from "immer";

270

271

// Objects and arrays are draftable

272

console.log(isDraftable({})); // true

273

console.log(isDraftable([])); // true

274

console.log(isDraftable({ name: "Alice" })); // true

275

276

// Primitives are not draftable

277

console.log(isDraftable(42)); // false

278

console.log(isDraftable("string")); // false

279

console.log(isDraftable(true)); // false

280

console.log(isDraftable(null)); // false

281

console.log(isDraftable(undefined)); // false

282

283

// Built-in objects vary

284

console.log(isDraftable(new Date())); // false

285

console.log(isDraftable(new RegExp("test"))); // false

286

console.log(isDraftable(new Map())); // false (unless enableMapSet() is called)

287

console.log(isDraftable(new Set())); // false (unless enableMapSet() is called)

288

289

// Functions are not draftable

290

console.log(isDraftable(() => {})); // false

291

292

// Class instances are not draftable by default

293

class MyClass {

294

constructor(public value: number) {}

295

}

296

console.log(isDraftable(new MyClass(42))); // false

297

298

// But can be made draftable with immerable symbol

299

import { immerable } from "immer";

300

301

class DraftableClass {

302

[immerable] = true;

303

constructor(public value: number) {}

304

}

305

console.log(isDraftable(new DraftableClass(42))); // true

306

307

// Useful for validation before creating drafts

308

function safeDraft<T>(value: T): T {

309

if (!isDraftable(value)) {

310

throw new Error("Value cannot be drafted");

311

}

312

return createDraft(value);

313

}

314

```

315

316

## Advanced Draft Patterns

317

318

```typescript

319

import { createDraft, finishDraft, current, original, isDraft } from "immer";

320

321

// Manual draft lifecycle management

322

function manualUpdate<T>(state: T, updater: (draft: Draft<T>) => void): T {

323

const draft = createDraft(state);

324

try {

325

updater(draft);

326

return finishDraft(draft);

327

} catch (error) {

328

// Draft is automatically cleaned up on error

329

throw error;

330

}

331

}

332

333

// Incremental draft building

334

function buildComplexState() {

335

const draft = createDraft({ items: [], metadata: {} as any });

336

337

// Phase 1: Add items

338

draft.items.push("item1", "item2");

339

340

// Phase 2: Add metadata based on current state

341

const currentItems = current(draft).items;

342

draft.metadata.count = currentItems.length;

343

draft.metadata.created = Date.now();

344

345

// Phase 3: Conditional updates based on accumulated state

346

if (current(draft).items.length > 1) {

347

draft.metadata.type = "multi-item";

348

}

349

350

return finishDraft(draft);

351

}

352

353

// Draft composition

354

function composeDrafts<T>(base: T, ...updaters: Array<(draft: Draft<T>) => void>): T {

355

let result = base;

356

357

for (const updater of updaters) {

358

result = produce(result, updater);

359

}

360

361

return result;

362

}

363

```

364

365

Draft management functions provide powerful tools for advanced Immer usage patterns where you need direct control over the draft lifecycle or want to build complex state transformations incrementally.