or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

extension-system.mddocs/

0

# Extension System

1

2

Advanced features for creating isolated contexts and adding custom commands. The extension system allows you to add your own commands to extend immutability-helper's functionality.

3

4

## Capabilities

5

6

### Context Class

7

8

The Context class provides an isolated environment for update operations with custom commands and equality functions.

9

10

```typescript { .api }

11

class Context {

12

/**

13

* Create a new Context instance with isolated command set

14

*/

15

constructor();

16

17

/**

18

* Add a custom command to this context

19

* @param directive - Command name (with $ prefix)

20

* @param fn - Function that implements the command

21

*/

22

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

23

24

/**

25

* Update function for this context

26

* @param object - Object to update

27

* @param spec - Update specification

28

* @returns Updated object

29

*/

30

update<T, C extends CustomCommands<object> = never>(

31

object: T,

32

$spec: Spec<T, C>

33

): T;

34

35

/**

36

* Get the equality function used for change detection

37

*/

38

get isEquals(): (x: any, y: any) => boolean;

39

40

/**

41

* Set a custom equality function for change detection

42

*/

43

set isEquals(value: (x: any, y: any) => boolean);

44

}

45

```

46

47

**Usage Examples:**

48

49

```typescript

50

import { Context } from "immutability-helper";

51

52

// Create isolated context

53

const myContext = new Context();

54

55

// Add custom command

56

myContext.extend('$addTax', function(tax, original) {

57

return original + (tax * original);

58

});

59

60

// Use custom context

61

const price = { amount: 100 };

62

const withTax = myContext.update(price, {

63

amount: { $addTax: 0.2 }

64

});

65

// Result: { amount: 120 }

66

67

// Custom equality function

68

myContext.isEquals = (a, b) => {

69

// Custom deep equality logic

70

return JSON.stringify(a) === JSON.stringify(b);

71

};

72

```

73

74

### Global Extend Function

75

76

Add custom commands to the default global context.

77

78

```typescript { .api }

79

/**

80

* Add a custom command to the global context

81

* @param directive - Command name (with $ prefix)

82

* @param fn - Function that implements the command

83

*/

84

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

85

```

86

87

**Usage Examples:**

88

89

```typescript

90

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

91

92

// Add global custom command

93

extend('$addTax', function(tax, original) {

94

return original + (tax * original);

95

});

96

97

// Use globally

98

const result = update({ price: 100 }, {

99

price: { $addTax: 0.15 }

100

});

101

// Result: { price: 115 }

102

103

// String manipulation command

104

extend('$capitalize', function(_, original) {

105

return typeof original === 'string'

106

? original.charAt(0).toUpperCase() + original.slice(1).toLowerCase()

107

: original;

108

});

109

110

const text = { title: 'hello world' };

111

const capitalized = update(text, {

112

title: { $capitalize: null }

113

});

114

// Result: { title: 'Hello world' }

115

```

116

117

## Advanced Extension Examples

118

119

### Mathematical Operations

120

121

```typescript

122

import { extend } from "immutability-helper";

123

124

// Add mathematical operations

125

extend('$multiply', (factor, original) => original * factor);

126

extend('$power', (exponent, original) => Math.pow(original, exponent));

127

extend('$round', (decimals, original) => {

128

const multiplier = Math.pow(10, decimals || 0);

129

return Math.round(original * multiplier) / multiplier;

130

});

131

132

const data = { value: 3.14159 };

133

const processed = update(data, {

134

value: { $round: 2 }

135

});

136

// Result: { value: 3.14 }

137

```

138

139

### Array Manipulation Commands

140

141

```typescript

142

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

143

144

// Custom array operations

145

extend('$shuffle', (_, original) => {

146

if (!Array.isArray(original)) return original;

147

const shuffled = [...original];

148

for (let i = shuffled.length - 1; i > 0; i--) {

149

const j = Math.floor(Math.random() * (i + 1));

150

[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];

151

}

152

return shuffled;

153

});

154

155

extend('$take', (count, original) => {

156

return Array.isArray(original) ? original.slice(0, count) : original;

157

});

158

159

const numbers = { list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] };

160

const taken = update(numbers, {

161

list: { $take: 3 }

162

});

163

// Result: { list: [1, 2, 3] }

164

```

165

166

### Conditional Operations

167

168

```typescript

169

import { extend } from "immutability-helper";

170

171

// Conditional update command

172

extend('$if', (condition, original) => {

173

const { test, then: thenSpec, else: elseSpec } = condition;

174

const shouldUpdate = typeof test === 'function' ? test(original) : test;

175

176

if (shouldUpdate && thenSpec) {

177

return update(original, thenSpec);

178

} else if (!shouldUpdate && elseSpec) {

179

return update(original, elseSpec);

180

}

181

182

return original;

183

});

184

185

const user = { age: 17, status: 'minor' };

186

const updated = update(user, {

187

$if: {

188

test: (obj) => obj.age >= 18,

189

then: { status: { $set: 'adult' } },

190

else: { status: { $set: 'minor' } }

191

}

192

});

193

```

194

195

### Autovivification Commands

196

197

Autovivification is the automatic creation of new arrays and objects when needed. JavaScript doesn't have this feature natively, which makes deep nested updates challenging. These custom commands solve this problem:

198

199

```typescript

200

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

201

202

// Auto-create missing nested structures

203

extend('$auto', function(value, object) {

204

return object ? update(object, value) : update({}, value);

205

});

206

207

extend('$autoArray', function(value, object) {

208

return object ? update(object, value) : update([], value);

209

});

210

211

// Usage example - building deep structures from empty state

212

const state = {};

213

const result = update(state, {

214

users: { $autoArray: {

215

0: { $auto: {

216

name: { $set: 'Alice' },

217

posts: { $autoArray: { $push: ['Hello World'] } }

218

}}

219

}}

220

});

221

// Result: { users: [{ name: 'Alice', posts: ['Hello World'] }] }

222

223

// Alternative approach without custom commands (manual autovivification)

224

const state2 = {};

225

const desiredState = {

226

foo: [{ bar: ['x', 'y', 'z'] }]

227

};

228

229

const manualResult = update(state2, {

230

foo: foo =>

231

update(foo || [], {

232

0: fooZero =>

233

update(fooZero || {}, {

234

bar: bar => update(bar || [], { $push: ["x", "y", "z"] })

235

})

236

})

237

});

238

// Result matches desiredState

239

```

240

241

## Custom Command Function Signature

242

243

Custom command functions receive three parameters:

244

245

```typescript

246

type CommandFunction<T> = (

247

param: any, // The parameter passed to the command

248

nextObject: T, // The current object being updated

249

spec: any, // The full specification object

250

originalObject: T // The original object before any updates

251

) => T;

252

```

253

254

**Advanced Command Example:**

255

256

```typescript

257

extend('$increment', function(amount, nextObject, spec, originalObject) {

258

// param: amount to increment

259

// nextObject: current value during update chain

260

// spec: the full spec object containing $increment

261

// originalObject: the original value before any updates

262

263

if (typeof nextObject === 'number') {

264

return nextObject + (amount || 1);

265

}

266

return nextObject;

267

});

268

```

269

270

## Type Safety with Custom Commands

271

272

For TypeScript users, you can create type-safe custom commands:

273

274

```typescript

275

import update, { CustomCommands, Spec } from "immutability-helper";

276

277

// Define custom command types

278

interface MyCommands {

279

$addTax: number;

280

$capitalize: null;

281

}

282

283

// Create typed update function

284

function myUpdate<T>(object: T, spec: Spec<T, CustomCommands<MyCommands>>) {

285

return update(object, spec);

286

}

287

288

// Usage with full type safety

289

const result = myUpdate({ price: 100, name: 'product' }, {

290

price: { $addTax: 0.2 }, // TypeScript knows this expects a number

291

name: { $capitalize: null } // TypeScript knows this expects null

292

});

293

```

294

295

## Error Handling

296

297

- Custom command functions should handle their own validation

298

- Use the `invariant` function for consistent error messages

299

- Commands that don't modify data should return the original object reference

300

301

```typescript

302

import { invariant } from "immutability-helper";

303

304

extend('$safeIncrement', function(amount, original) {

305

invariant(

306

typeof original === 'number',

307

() => `$safeIncrement expects a number, got ${typeof original}`

308

);

309

310

invariant(

311

typeof amount === 'number',

312

() => `$safeIncrement amount must be a number, got ${typeof amount}`

313

);

314

315

return original + amount;

316

});

317

```

318

319

## Performance Considerations

320

321

- Custom commands should preserve reference equality when no changes occur

322

- Avoid expensive operations in frequently called commands

323

- Consider using the fourth parameter (`originalObject`) for optimization decisions

324

- Custom equality functions affect performance - keep them fast and consistent