@helpers4/object 3.0.0-alpha.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/index.d.ts +314 -58
- package/lib/index.js +252 -23
- package/lib/index.js.map +1 -1
- package/llms.txt +381 -2
- package/meta/api.json +351 -1
- package/meta/examples.json +120 -0
- package/package.json +10 -2
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @helpers4/object
|
|
2
2
|
|
|
3
3
|
> Tree-shakable TypeScript utility functions for the `object` domain.
|
|
4
|
-
> Package: `@helpers4/object` — Version: 3.0.0
|
|
4
|
+
> Package: `@helpers4/object` — Version: 3.0.0
|
|
5
5
|
> License: LGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
@@ -15,18 +15,20 @@ pnpm add @helpers4/object
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { cloneDeep, compact,
|
|
18
|
+
import { clone, cloneDeep, compact, ... } from '@helpers4/object';
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Functions
|
|
22
22
|
|
|
23
23
|
| Function | Description |
|
|
24
24
|
|---|---|
|
|
25
|
+
| `clone` | Creates a shallow copy of a value — one level deep, unlike cloneDeep. Unlike a plain `{ ...value }` |
|
|
25
26
|
| `cloneDeep` | Creates a deep copy of an object or array. Recursively clones own enumerable string keys. `Date` in |
|
|
26
27
|
| `compact` | Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an objec |
|
|
27
28
|
| `diff` | Structural object diff. Returns `true` when both inputs are deeply equal, otherwise a DiffResult de |
|
|
28
29
|
| `equalsDeep` | Recursive structural object equality. Boolean wrapper around diff \u2014 returns `true` when the tw |
|
|
29
30
|
| `equalsShallow` | One-level (shallow) object equality. Two objects are equal when they share the exact same set of ow |
|
|
31
|
+
| `flatten` | Flattens a nested object into a single-level object whose keys are the dot-notation path to each lea |
|
|
30
32
|
| `get` | Gets a value from an object using a dot/bracket-notated path or explicit key array. **Two path form |
|
|
31
33
|
| `groupBy` | Groups an array of items by a key derived from each item. A thin, typed wrapper around `Object.grou |
|
|
32
34
|
| `invert` | Returns a new object with keys and values swapped. If multiple keys share the same value, the last o |
|
|
@@ -35,15 +37,67 @@ import { cloneDeep, compact, diff, ... } from '@helpers4/object';
|
|
|
35
37
|
| `map` | Transforms the values and/or keys of a plain object in a single pass. Both callbacks are optional a |
|
|
36
38
|
| `mergeDeep` | Merges two or more objects deeply, returning a **new** object without mutating any input. Sources a |
|
|
37
39
|
| `omit` | Creates a new object without the specified keys. |
|
|
40
|
+
| `omitBy` | Creates a new object without the own enumerable entries for which `predicate` returns `true`. Compl |
|
|
41
|
+
| `parsePropertyPath` | Parses a dot/bracket-notation property path into an array of string/number key segments — the same n |
|
|
38
42
|
| `pick` | Creates a new object with only the specified keys. Keys that are prototype-polluting strings (`__pro |
|
|
43
|
+
| `pickBy` | Creates a new object with only the own enumerable entries for which `predicate` returns `true`. Com |
|
|
39
44
|
| `removeUndefinedNull` | Remove null and undefined values from an object. |
|
|
40
45
|
| `safeJsonParse` | Parses a JSON string, returning `null` (or a fallback) on any parse failure. Unlike `JSON.parse`, t |
|
|
41
46
|
| `set` | Sets a value in an object at the given path, creating intermediate objects as needed. **Two path fo |
|
|
47
|
+
| `unflatten` | Rebuilds a nested object from a single-level object whose keys are dot-notation paths. The inverse o |
|
|
48
|
+
| `unset` | Removes the value at a dot/bracket-notation path or explicit key array, mutating the object in place |
|
|
49
|
+
| `update` | Updates the value at a path by applying a function to its current value, creating intermediate objec |
|
|
42
50
|
|
|
43
51
|
---
|
|
44
52
|
|
|
45
53
|
## API Reference
|
|
46
54
|
|
|
55
|
+
### `clone`
|
|
56
|
+
|
|
57
|
+
Creates a shallow copy of a value — one level deep, unlike cloneDeep.
|
|
58
|
+
|
|
59
|
+
Unlike a plain `{ ...value }` spread, this correctly reconstructs `Date`,
|
|
60
|
+
`Map`, `Set`, and arrays instead of producing an empty (or wrong-shaped)
|
|
61
|
+
plain object for them. Primitives are returned as-is. Any other object
|
|
62
|
+
(including class instances not listed above) has its own enumerable string
|
|
63
|
+
keys shallow-copied into a plain object — the same fallback `cloneDeep`
|
|
64
|
+
uses, so the two stay consistent for types neither one special-cases.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { clone } from '@helpers4/object';
|
|
68
|
+
|
|
69
|
+
clone<T>(value: T): T
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Parameters:**
|
|
73
|
+
|
|
74
|
+
- `value: T` — The value to shallow-clone
|
|
75
|
+
|
|
76
|
+
**Returns:** `T` — A shallow copy of `value`
|
|
77
|
+
|
|
78
|
+
**Examples:**
|
|
79
|
+
|
|
80
|
+
*Shallow-copy a plain object*
|
|
81
|
+
|
|
82
|
+
Top-level keys are copied; nested values still share references (shallow).
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const obj = { a: 1, b: { c: 2 } };
|
|
86
|
+
const copy = clone(obj);
|
|
87
|
+
copy.b === obj.b // => true (same nested reference)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
*Correctly clones Date/Map/Set, unlike a spread*
|
|
91
|
+
|
|
92
|
+
{ ...new Date() } produces {} — clone() doesn't have that problem.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
clone(new Map([['a', 1]]))
|
|
96
|
+
// => new Map with the same entries, not {}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
47
101
|
### `cloneDeep`
|
|
48
102
|
|
|
49
103
|
Creates a deep copy of an object or array.
|
|
@@ -290,6 +344,55 @@ equalsShallow({ a: 1, b: 2 }, { a: 1, b: 2 })
|
|
|
290
344
|
|
|
291
345
|
---
|
|
292
346
|
|
|
347
|
+
### `flatten`
|
|
348
|
+
|
|
349
|
+
Flattens a nested object into a single-level object whose keys are the
|
|
350
|
+
dot-notation path to each leaf value. The inverse of unflatten.
|
|
351
|
+
|
|
352
|
+
Only **plain objects** are recursed into — arrays, `Date`, `Map`, `RegExp`,
|
|
353
|
+
class instances, and empty plain objects `{}` are kept as opaque leaf
|
|
354
|
+
values. This keeps `flatten`/`unflatten` a clean, invertible pair: arrays
|
|
355
|
+
can't be losslessly told apart from plain objects once reduced to dotted
|
|
356
|
+
keys, so this implementation doesn't attempt it.
|
|
357
|
+
|
|
358
|
+
Caveat shared by every dotted-path flattening scheme: a key that itself
|
|
359
|
+
contains a literal `.` is indistinguishable from real nesting once
|
|
360
|
+
flattened (`{ 'a.b': 1 }` and `{ a: { b: 1 } }` both produce `{ 'a.b': 1 }`).
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { flatten } from '@helpers4/object';
|
|
364
|
+
|
|
365
|
+
flatten(obj: Record<string, unknown>): Record<string, unknown>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Parameters:**
|
|
369
|
+
|
|
370
|
+
- `obj: Record<string, unknown>` — The object to flatten
|
|
371
|
+
|
|
372
|
+
**Returns:** `Record<string, unknown>` — A single-level object with dot-notation keys
|
|
373
|
+
|
|
374
|
+
**Examples:**
|
|
375
|
+
|
|
376
|
+
*Flatten a nested config object*
|
|
377
|
+
|
|
378
|
+
Each key in the result is the full dot-notation path to a leaf value.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
flatten({ server: { host: 'localhost', port: 8080 } })
|
|
382
|
+
// => { 'server.host': 'localhost', 'server.port': 8080 }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
*Arrays and special objects are kept as leaves*
|
|
386
|
+
|
|
387
|
+
Only plain objects are recursed into — arrays stay intact.
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
flatten({ tags: ['a', 'b'], meta: { owner: 'x' } })
|
|
391
|
+
// => { tags: ['a', 'b'], 'meta.owner': 'x' }
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
293
396
|
### `get`
|
|
294
397
|
|
|
295
398
|
Gets a value from an object using a dot/bracket-notated path or explicit key array.
|
|
@@ -762,6 +865,98 @@ omit(user, ['password', 'token'])
|
|
|
762
865
|
|
|
763
866
|
---
|
|
764
867
|
|
|
868
|
+
### `omitBy`
|
|
869
|
+
|
|
870
|
+
Creates a new object without the own enumerable entries for which
|
|
871
|
+
`predicate` returns `true`.
|
|
872
|
+
|
|
873
|
+
Complements omit for when the keys to remove aren't known ahead of
|
|
874
|
+
time — `omit` takes an explicit key list, `omitBy` takes a predicate.
|
|
875
|
+
|
|
876
|
+
```typescript
|
|
877
|
+
import { omitBy } from '@helpers4/object';
|
|
878
|
+
|
|
879
|
+
omitBy<T extends Record<string, unknown>>(obj: T | null | undefined, predicate: function): Partial<T> | null | undefined
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**Parameters:**
|
|
883
|
+
|
|
884
|
+
- `obj: T | null | undefined` — The source object. `null`/`undefined` pass through unchanged.
|
|
885
|
+
- `predicate: function` — Called with `(value, key)` for each own enumerable entry
|
|
886
|
+
|
|
887
|
+
**Returns:** `Partial<T> | null | undefined` — A new object without the matching entries
|
|
888
|
+
|
|
889
|
+
**Examples:**
|
|
890
|
+
|
|
891
|
+
*Drop entries matching a predicate*
|
|
892
|
+
|
|
893
|
+
Unlike omit(), the keys to drop don’t need to be known ahead of time.
|
|
894
|
+
|
|
895
|
+
```typescript
|
|
896
|
+
omitBy({ a: 1, b: undefined, c: 2 }, (value) => value === undefined)
|
|
897
|
+
// => { a: 1, c: 2 }
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
*Drop entries by key name*
|
|
901
|
+
|
|
902
|
+
The predicate also receives the key, so filtering by name works too.
|
|
903
|
+
|
|
904
|
+
```typescript
|
|
905
|
+
omitBy({ id: 1, name: 'x', _internal: true }, (_v, key) => key.startsWith('_'))
|
|
906
|
+
// => { id: 1, name: 'x' }
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
### `parsePropertyPath`
|
|
912
|
+
|
|
913
|
+
Parses a dot/bracket-notation property path into an array of string/number
|
|
914
|
+
key segments — the same notation accepted by get and set.
|
|
915
|
+
|
|
916
|
+
- Dot separators (`.`) split segments; each segment becomes a string key.
|
|
917
|
+
- Bracket indices (`[n]`) become number keys.
|
|
918
|
+
- A leading `.` is treated as "current level" and stripped before parsing,
|
|
919
|
+
so `.[0]` ≡ `[0]` and `.a.b` ≡ `a.b`.
|
|
920
|
+
- Empty string (or a bare `.`) returns `['']` (addresses the `''` key on the root object).
|
|
921
|
+
- Consecutive dots (`a..b`) produce an empty-string segment: `['a', '', 'b']`.
|
|
922
|
+
|
|
923
|
+
Results are cached (up to 500 distinct path strings, oldest evicted first)
|
|
924
|
+
since real-world callers tend to reuse a small, fixed set of literal paths.
|
|
925
|
+
|
|
926
|
+
```typescript
|
|
927
|
+
import { parsePropertyPath } from '@helpers4/object';
|
|
928
|
+
|
|
929
|
+
parsePropertyPath(path: string): readonly string | number[]
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
**Parameters:**
|
|
933
|
+
|
|
934
|
+
- `path: string` — The dot/bracket-notation path string to parse
|
|
935
|
+
|
|
936
|
+
**Returns:** `readonly string | number[]` — The parsed key segments
|
|
937
|
+
|
|
938
|
+
**Examples:**
|
|
939
|
+
|
|
940
|
+
*Parse a dot/bracket-notation path into key segments*
|
|
941
|
+
|
|
942
|
+
The same notation accepted by get() and set() — bracket indices become numbers.
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
parsePropertyPath('layers[1].name')
|
|
946
|
+
// => ['layers', 1, 'name']
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
*Malformed paths throw instead of silently misparsing*
|
|
950
|
+
|
|
951
|
+
Text trailing a closing bracket within a segment is ambiguous — use 'a[0].b' instead.
|
|
952
|
+
|
|
953
|
+
```typescript
|
|
954
|
+
parsePropertyPath('a[0]b')
|
|
955
|
+
// => throws RangeError
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
---
|
|
959
|
+
|
|
765
960
|
### `pick`
|
|
766
961
|
|
|
767
962
|
Creates a new object with only the specified keys.
|
|
@@ -830,6 +1025,49 @@ pick(user, ['id', 'name', 'email'])
|
|
|
830
1025
|
|
|
831
1026
|
---
|
|
832
1027
|
|
|
1028
|
+
### `pickBy`
|
|
1029
|
+
|
|
1030
|
+
Creates a new object with only the own enumerable entries for which
|
|
1031
|
+
`predicate` returns `true`.
|
|
1032
|
+
|
|
1033
|
+
Complements pick for when the keys to keep aren't known ahead of
|
|
1034
|
+
time — `pick` takes an explicit key list, `pickBy` takes a predicate.
|
|
1035
|
+
|
|
1036
|
+
```typescript
|
|
1037
|
+
import { pickBy } from '@helpers4/object';
|
|
1038
|
+
|
|
1039
|
+
pickBy<T extends Record<string, unknown>>(obj: T | null | undefined, predicate: function): Partial<T> | null | undefined
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
**Parameters:**
|
|
1043
|
+
|
|
1044
|
+
- `obj: T | null | undefined` — The source object. `null`/`undefined` pass through unchanged.
|
|
1045
|
+
- `predicate: function` — Called with `(value, key)` for each own enumerable entry
|
|
1046
|
+
|
|
1047
|
+
**Returns:** `Partial<T> | null | undefined` — A new object with only the matching entries
|
|
1048
|
+
|
|
1049
|
+
**Examples:**
|
|
1050
|
+
|
|
1051
|
+
*Keep only entries matching a predicate*
|
|
1052
|
+
|
|
1053
|
+
Unlike pick(), the keys to keep don’t need to be known ahead of time.
|
|
1054
|
+
|
|
1055
|
+
```typescript
|
|
1056
|
+
pickBy({ a: 1, b: 0, c: 2 }, (value) => value > 0)
|
|
1057
|
+
// => { a: 1, c: 2 }
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
*Keep entries by key name*
|
|
1061
|
+
|
|
1062
|
+
The predicate also receives the key, so filtering by name works too.
|
|
1063
|
+
|
|
1064
|
+
```typescript
|
|
1065
|
+
pickBy({ _id: 1, name: 'x', _internal: true }, (_v, key) => !key.startsWith('_'))
|
|
1066
|
+
// => { name: 'x' }
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
833
1071
|
### `removeUndefinedNull`
|
|
834
1072
|
|
|
835
1073
|
Remove null and undefined values from an object.
|
|
@@ -1017,3 +1255,144 @@ set({}, ['user', id], 'alice')
|
|
|
1017
1255
|
```
|
|
1018
1256
|
|
|
1019
1257
|
---
|
|
1258
|
+
|
|
1259
|
+
### `unflatten`
|
|
1260
|
+
|
|
1261
|
+
Rebuilds a nested object from a single-level object whose keys are
|
|
1262
|
+
dot-notation paths. The inverse of flatten.
|
|
1263
|
+
|
|
1264
|
+
Uses set internally, so intermediate nodes are always created as
|
|
1265
|
+
plain objects (never arrays — see flatten's doc for why), and any
|
|
1266
|
+
key segment equal to `__proto__`, `constructor`, or `prototype` is silently
|
|
1267
|
+
rejected (same prototype-pollution guard as `set`).
|
|
1268
|
+
|
|
1269
|
+
```typescript
|
|
1270
|
+
import { unflatten } from '@helpers4/object';
|
|
1271
|
+
|
|
1272
|
+
unflatten(obj: Record<string, unknown>): Record<string, unknown>
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
**Parameters:**
|
|
1276
|
+
|
|
1277
|
+
- `obj: Record<string, unknown>` — A single-level object with dot-notation keys
|
|
1278
|
+
|
|
1279
|
+
**Returns:** `Record<string, unknown>` — The rebuilt nested object
|
|
1280
|
+
|
|
1281
|
+
**Examples:**
|
|
1282
|
+
|
|
1283
|
+
*Rebuild a nested object from dotted keys*
|
|
1284
|
+
|
|
1285
|
+
The inverse of flatten() — useful for parsing flat config sources (env vars, .ini files).
|
|
1286
|
+
|
|
1287
|
+
```typescript
|
|
1288
|
+
unflatten({ 'server.host': 'localhost', 'server.port': 8080 })
|
|
1289
|
+
// => { server: { host: 'localhost', port: 8080 } }
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
*Multiple keys under the same parent are merged*
|
|
1293
|
+
|
|
1294
|
+
Every dotted key contributes to the same nested structure.
|
|
1295
|
+
|
|
1296
|
+
```typescript
|
|
1297
|
+
unflatten({ 'a.x': 1, 'a.y': 2 })
|
|
1298
|
+
// => { a: { x: 1, y: 2 } }
|
|
1299
|
+
```
|
|
1300
|
+
|
|
1301
|
+
---
|
|
1302
|
+
|
|
1303
|
+
### `unset`
|
|
1304
|
+
|
|
1305
|
+
Removes the value at a dot/bracket-notation path or explicit key array,
|
|
1306
|
+
mutating the object in place. Uses the same path syntax as get/set.
|
|
1307
|
+
|
|
1308
|
+
A missing intermediate segment is a no-op (nothing to remove), not an error.
|
|
1309
|
+
As with `set`, any path containing a string segment equal to `__proto__`,
|
|
1310
|
+
`constructor`, or `prototype` is rejected and the object is returned unchanged.
|
|
1311
|
+
|
|
1312
|
+
The removed key stops appearing in `Object.keys`/`for...in` — unlike setting
|
|
1313
|
+
it to `undefined`, which would keep the key present.
|
|
1314
|
+
|
|
1315
|
+
```typescript
|
|
1316
|
+
import { unset } from '@helpers4/object';
|
|
1317
|
+
|
|
1318
|
+
unset<T extends object>(obj: T, path: string | readonly PropertyKey[]): T
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
**Parameters:**
|
|
1322
|
+
|
|
1323
|
+
- `obj: T` — The object to mutate
|
|
1324
|
+
- `path: string | readonly PropertyKey[]` — Dot/bracket-notation string, or explicit `PropertyKey[]`
|
|
1325
|
+
|
|
1326
|
+
**Returns:** `T` — The same object reference, with the key removed if it existed
|
|
1327
|
+
|
|
1328
|
+
**Examples:**
|
|
1329
|
+
|
|
1330
|
+
*Remove a nested value by path*
|
|
1331
|
+
|
|
1332
|
+
Uses the same dot/bracket path syntax as get() and set().
|
|
1333
|
+
|
|
1334
|
+
```typescript
|
|
1335
|
+
const config = { server: { host: 'localhost', debug: true } };
|
|
1336
|
+
unset(config, 'server.debug')
|
|
1337
|
+
// => { server: { host: 'localhost' } }
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
*Missing paths are a safe no-op*
|
|
1341
|
+
|
|
1342
|
+
Unlike set(), unset() never creates intermediate objects — there is nothing to remove.
|
|
1343
|
+
|
|
1344
|
+
```typescript
|
|
1345
|
+
unset({ a: 1 }, 'x.y.z')
|
|
1346
|
+
// => { a: 1 } (unchanged)
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
---
|
|
1350
|
+
|
|
1351
|
+
### `update`
|
|
1352
|
+
|
|
1353
|
+
Updates the value at a path by applying a function to its current value,
|
|
1354
|
+
creating intermediate objects as needed. Equivalent to
|
|
1355
|
+
`set(obj, path, updater(get(obj, path)))` in a single call.
|
|
1356
|
+
|
|
1357
|
+
Uses the same path syntax and type-inference rules as get and
|
|
1358
|
+
set — see those for the full behavior (string vs. `PropertyKey[]`
|
|
1359
|
+
paths, prototype-pollution guarding, etc.).
|
|
1360
|
+
|
|
1361
|
+
```typescript
|
|
1362
|
+
import { update } from '@helpers4/object';
|
|
1363
|
+
|
|
1364
|
+
update<T extends object, P extends string | readonly PropertyKey[], V extends unknown>(obj: T, path: P, updater: function): conditional
|
|
1365
|
+
```
|
|
1366
|
+
|
|
1367
|
+
**Parameters:**
|
|
1368
|
+
|
|
1369
|
+
- `obj: T` — The object to mutate
|
|
1370
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
1371
|
+
- `updater: function` — Called with the current value (`undefined` if the path is
|
|
1372
|
+
absent); its return value is written back at the path
|
|
1373
|
+
|
|
1374
|
+
**Returns:** `conditional` — The mutated object (same reference, narrowed type)
|
|
1375
|
+
|
|
1376
|
+
**Examples:**
|
|
1377
|
+
|
|
1378
|
+
*Increment a counter in one call*
|
|
1379
|
+
|
|
1380
|
+
Equivalent to set(obj, path, updater(get(obj, path))), without repeating the path.
|
|
1381
|
+
|
|
1382
|
+
```typescript
|
|
1383
|
+
const state = { count: 1 };
|
|
1384
|
+
update(state, 'count', (n) => (n ?? 0) + 1)
|
|
1385
|
+
// => { count: 2 }
|
|
1386
|
+
```
|
|
1387
|
+
|
|
1388
|
+
*Missing paths create intermediate objects, like set()*
|
|
1389
|
+
|
|
1390
|
+
The updater receives undefined when the path does not exist yet.
|
|
1391
|
+
|
|
1392
|
+
```typescript
|
|
1393
|
+
const stats: Record<string, unknown> = {};
|
|
1394
|
+
update(stats, 'hits.total', (n: number | undefined) => (n ?? 0) + 1)
|
|
1395
|
+
// => { hits: { total: 1 } }
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
---
|