@helpers4/object 2.0.3 → 2.1.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 +2 -2
- package/lib/index.d.ts +295 -50
- package/lib/index.js +163 -76
- package/lib/index.js.map +1 -1
- package/llms.txt +239 -122
- package/meta/api.json +207 -169
- package/meta/examples.json +38 -23
- package/meta/native-alternatives.json +1 -1
- package/package.json +3 -3
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: 2.0
|
|
4
|
+
> Package: `@helpers4/object` — Version: 2.1.0
|
|
5
5
|
> License: LGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
@@ -15,35 +15,74 @@ pnpm add @helpers4/object
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
18
|
+
import { cloneDeep, compact, diff, ... } from '@helpers4/object';
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Functions
|
|
22
22
|
|
|
23
23
|
| Function | Description |
|
|
24
24
|
|---|---|
|
|
25
|
+
| `cloneDeep` | Creates a deep copy of an object or array. Recursively clones own enumerable string keys. `Date` in |
|
|
25
26
|
| `compact` | Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an objec |
|
|
26
|
-
| `deepClone` | Creates a deep copy of an object or array |
|
|
27
|
-
| `deepMerge` | Merges two or more objects deeply |
|
|
28
27
|
| `diff` | Structural object diff. Returns `true` when both inputs are deeply equal, otherwise a DiffResult de |
|
|
29
28
|
| `equalsDeep` | Recursive structural object equality. Boolean wrapper around diff \u2014 returns `true` when the tw |
|
|
30
29
|
| `equalsShallow` | One-level (shallow) object equality. Two objects are equal when they share the exact same set of ow |
|
|
31
|
-
| `get` | Gets a value from an object using a dot-notated path |
|
|
30
|
+
| `get` | Gets a value from an object using a dot/bracket-notated path or explicit key array. **Two path form |
|
|
32
31
|
| `groupBy` | Groups an array of items by a key derived from each item. A thin, typed wrapper around `Object.grou |
|
|
33
32
|
| `invert` | Returns a new object with keys and values swapped. If multiple keys share the same value, the last o |
|
|
34
|
-
| `isEmpty` | Checks if a plain object has no own enumerable string-keyed properties.
|
|
35
|
-
| `isNonEmpty` | Checks if a plain object has at least one own enumerable string-keyed property.
|
|
33
|
+
| `isEmpty` | Checks if a plain object has no own enumerable string-keyed properties. `null` and `undefined` are t |
|
|
34
|
+
| `isNonEmpty` | Checks if a plain object has at least one own enumerable string-keyed property. `null` and `undefine |
|
|
36
35
|
| `map` | Transforms the values and/or keys of a plain object in a single pass. Both callbacks are optional a |
|
|
36
|
+
| `mergeDeep` | Merges two or more objects deeply, returning a **new** object without mutating any input. Sources a |
|
|
37
37
|
| `omit` | Creates a new object without the specified keys. |
|
|
38
38
|
| `pick` | Creates a new object with only the specified keys. |
|
|
39
39
|
| `removeUndefinedNull` | Remove null and undefined values from an object. |
|
|
40
40
|
| `safeJsonParse` | Parses a JSON string, returning `null` (or a fallback) on any parse failure. Unlike `JSON.parse`, t |
|
|
41
|
-
| `set` | Sets a value in an object
|
|
41
|
+
| `set` | Sets a value in an object at the given path, creating intermediate objects as needed. **Two path fo |
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
45
|
## API Reference
|
|
46
46
|
|
|
47
|
+
### `cloneDeep`
|
|
48
|
+
|
|
49
|
+
Creates a deep copy of an object or array.
|
|
50
|
+
|
|
51
|
+
Recursively clones own enumerable string keys. `Date` instances are
|
|
52
|
+
reconstructed with the same epoch value. Prototype-polluting keys
|
|
53
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
54
|
+
Does **not** handle circular references.
|
|
55
|
+
|
|
56
|
+
**Limitation:** symbol-keyed own properties are **not** copied — only string
|
|
57
|
+
keys are processed. Use `structuredClone` if symbol propagation is required.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { cloneDeep } from '@helpers4/object';
|
|
61
|
+
|
|
62
|
+
cloneDeep<T>(obj: T): T
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Parameters:**
|
|
66
|
+
|
|
67
|
+
- `obj: T` — The value to clone
|
|
68
|
+
|
|
69
|
+
**Returns:** `T` — A deep copy of `obj`
|
|
70
|
+
|
|
71
|
+
**Examples:**
|
|
72
|
+
|
|
73
|
+
*Clone a nested object*
|
|
74
|
+
|
|
75
|
+
Creates a deep copy — modifying the clone does not affect the original.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const original = { a: { b: 1 } };
|
|
79
|
+
const cloned = cloneDeep(original);
|
|
80
|
+
cloned.a.b = 2;
|
|
81
|
+
// original.a.b is still 1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
47
86
|
### `compact`
|
|
48
87
|
|
|
49
88
|
Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an object.
|
|
@@ -106,93 +145,6 @@ compact({ name: 'Alice', email: '', age: 0, role: 'admin' })
|
|
|
106
145
|
|
|
107
146
|
---
|
|
108
147
|
|
|
109
|
-
### `deepClone`
|
|
110
|
-
|
|
111
|
-
Creates a deep copy of an object or array
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { deepClone } from '@helpers4/object';
|
|
115
|
-
|
|
116
|
-
deepClone<T>(obj: T): T
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**Parameters:**
|
|
120
|
-
|
|
121
|
-
- `obj: T` — The object to clone
|
|
122
|
-
|
|
123
|
-
**Returns:** `T` — Deep cloned object
|
|
124
|
-
|
|
125
|
-
**Examples:**
|
|
126
|
-
|
|
127
|
-
*Clone a nested object*
|
|
128
|
-
|
|
129
|
-
Creates a deep copy — modifying the clone does not affect the original.
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
const original = { a: { b: 1 } };
|
|
133
|
-
const cloned = deepClone(original);
|
|
134
|
-
cloned.a.b = 2;
|
|
135
|
-
// original.a.b is still 1
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
### `deepMerge`
|
|
141
|
-
|
|
142
|
-
Merges two or more objects deeply
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { deepMerge } from '@helpers4/object';
|
|
146
|
-
|
|
147
|
-
deepMerge<T extends Record<string, unknown>>(target: T, sources: Record<string, unknown>[]): T
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**Parameters:**
|
|
151
|
-
|
|
152
|
-
- `target: T` — The target object
|
|
153
|
-
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
154
|
-
|
|
155
|
-
**Returns:** `T` — The merged object
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
import { deepMerge } from '@helpers4/object';
|
|
159
|
-
|
|
160
|
-
deepMerge(target: undefined, sources: Record<string, unknown>[]): undefined
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**Parameters:**
|
|
164
|
-
|
|
165
|
-
- `target: undefined` — The target object
|
|
166
|
-
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
167
|
-
|
|
168
|
-
**Returns:** `undefined` — The merged object
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { deepMerge } from '@helpers4/object';
|
|
172
|
-
|
|
173
|
-
deepMerge(target: null, sources: Record<string, unknown>[]): null
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Parameters:**
|
|
177
|
-
|
|
178
|
-
- `target: null` — The target object
|
|
179
|
-
- `sources: Record<string, unknown>[]` — The source objects to merge
|
|
180
|
-
|
|
181
|
-
**Returns:** `null` — The merged object
|
|
182
|
-
|
|
183
|
-
**Examples:**
|
|
184
|
-
|
|
185
|
-
*Merge two objects deeply*
|
|
186
|
-
|
|
187
|
-
Recursively merges source properties into the target object.
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
191
|
-
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
148
|
### `diff`
|
|
197
149
|
|
|
198
150
|
Structural object diff.
|
|
@@ -338,21 +290,64 @@ equalsShallow({ a: 1, b: 2 }, { a: 1, b: 2 })
|
|
|
338
290
|
|
|
339
291
|
### `get`
|
|
340
292
|
|
|
341
|
-
Gets a value from an object using a dot-notated path
|
|
293
|
+
Gets a value from an object using a dot/bracket-notated path or explicit key array.
|
|
294
|
+
|
|
295
|
+
**Two path forms are supported:**
|
|
296
|
+
|
|
297
|
+
1. **String path** — dot notation (`'a.b.c'`) and bracket notation (`'layers[1].name'`)
|
|
298
|
+
are both accepted and mixed freely.
|
|
299
|
+
- Dot segments are always string keys: `'a.1'` → key `'1'` (string).
|
|
300
|
+
- Bracket segments are always number keys: `'a[1]'` → key `1` (number).
|
|
301
|
+
- String literal paths give **full compile-time type inference** on the return type.
|
|
302
|
+
- Dynamic (non-literal) strings return `unknown`.
|
|
303
|
+
|
|
304
|
+
2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
305
|
+
no parsing performed. Enables symbol-keyed traversal and compile-time type inference:
|
|
306
|
+
`get(obj, ['a', 'b'] as const)` infers the return type from the path.
|
|
307
|
+
|
|
308
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
309
|
+
Symbol keys are only reachable via the key-array form.
|
|
342
310
|
|
|
343
311
|
```typescript
|
|
344
312
|
import { get } from '@helpers4/object';
|
|
345
313
|
|
|
346
|
-
get<
|
|
314
|
+
get<D>(obj: null | undefined, path: string | readonly PropertyKey[], defaultValue: D): D
|
|
347
315
|
```
|
|
348
316
|
|
|
349
317
|
**Parameters:**
|
|
350
318
|
|
|
351
|
-
- `obj:
|
|
352
|
-
- `path: string` —
|
|
353
|
-
- `defaultValue
|
|
319
|
+
- `obj: null | undefined` — The object to read from
|
|
320
|
+
- `path: string | readonly PropertyKey[]` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
321
|
+
- `defaultValue: D` — Returned when the path is absent or resolves to `undefined`
|
|
322
|
+
|
|
323
|
+
**Returns:** `D` — The value at the path, or `defaultValue`
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { get } from '@helpers4/object';
|
|
327
|
+
|
|
328
|
+
get(obj: null | undefined, path: string | readonly PropertyKey[]): undefined
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Parameters:**
|
|
354
332
|
|
|
355
|
-
|
|
333
|
+
- `obj: null | undefined` — The object to read from
|
|
334
|
+
- `path: string | readonly PropertyKey[]` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
335
|
+
|
|
336
|
+
**Returns:** `undefined` — The value at the path, or `defaultValue`
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { get } from '@helpers4/object';
|
|
340
|
+
|
|
341
|
+
get<T extends object, P extends string | readonly PropertyKey[]>(obj: T | null | undefined, path: P, defaultValue?: DeepGet<T, ParsePath<P>>): DeepGet<T, ParsePath<P>> | undefined
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Parameters:**
|
|
345
|
+
|
|
346
|
+
- `obj: T | null | undefined` — The object to read from
|
|
347
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
348
|
+
- `defaultValue?: DeepGet<T, ParsePath<P>>` — Returned when the path is absent or resolves to `undefined`
|
|
349
|
+
|
|
350
|
+
**Returns:** `DeepGet<T, ParsePath<P>> | undefined` — The value at the path, or `defaultValue`
|
|
356
351
|
|
|
357
352
|
**Examples:**
|
|
358
353
|
|
|
@@ -374,6 +369,16 @@ get({ a: 1 }, 'b.c', 'default')
|
|
|
374
369
|
// => 'default'
|
|
375
370
|
```
|
|
376
371
|
|
|
372
|
+
*Get via key array (supports symbols)*
|
|
373
|
+
|
|
374
|
+
Pass an explicit PropertyKey[] to bypass parsing. Supports string, number, and symbol keys.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
const id = Symbol('id')
|
|
378
|
+
get({ [id]: 'alice' }, [id])
|
|
379
|
+
// => 'alice'
|
|
380
|
+
```
|
|
381
|
+
|
|
377
382
|
---
|
|
378
383
|
|
|
379
384
|
### `groupBy`
|
|
@@ -382,16 +387,19 @@ Groups an array of items by a key derived from each item.
|
|
|
382
387
|
|
|
383
388
|
A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
384
389
|
older targets and provides stricter return-type inference.
|
|
390
|
+
`null` and `undefined` are treated as empty arrays and return `{}`.
|
|
391
|
+
Items whose computed key is a prototype-polluting string (`__proto__`,
|
|
392
|
+
`constructor`, `prototype`) are silently skipped.
|
|
385
393
|
|
|
386
394
|
```typescript
|
|
387
395
|
import { groupBy } from '@helpers4/object';
|
|
388
396
|
|
|
389
|
-
groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: function): Partial<Record<K, T[]>>
|
|
397
|
+
groupBy<T, K extends PropertyKey>(items: readonly T[] | null | undefined, keyFn: function): Partial<Record<K, T[]>>
|
|
390
398
|
```
|
|
391
399
|
|
|
392
400
|
**Parameters:**
|
|
393
401
|
|
|
394
|
-
- `items: readonly T[]` — The array to group
|
|
402
|
+
- `items: readonly T[] | null | undefined` — The array to group
|
|
395
403
|
- `keyFn: function` — Function that returns the group key for each item
|
|
396
404
|
|
|
397
405
|
**Returns:** `Partial<Record<K, T[]>>` — A record mapping each key to the array of items with that key
|
|
@@ -427,16 +435,19 @@ groupBy(users, u => u.role)
|
|
|
427
435
|
|
|
428
436
|
Returns a new object with keys and values swapped.
|
|
429
437
|
If multiple keys share the same value, the last one wins.
|
|
438
|
+
`null` and `undefined` are treated as empty objects and return `{}`.
|
|
439
|
+
Entries whose source key **or** value is a prototype-polluting string
|
|
440
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
430
441
|
|
|
431
442
|
```typescript
|
|
432
443
|
import { invert } from '@helpers4/object';
|
|
433
444
|
|
|
434
|
-
invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>
|
|
445
|
+
invert<K extends string, V extends PropertyKey>(obj: Record<K, V> | null | undefined): Record<V, K>
|
|
435
446
|
```
|
|
436
447
|
|
|
437
448
|
**Parameters:**
|
|
438
449
|
|
|
439
|
-
- `obj: Record<K, V
|
|
450
|
+
- `obj: Record<K, V> | null | undefined` — The object whose keys and values are to be swapped
|
|
440
451
|
|
|
441
452
|
**Returns:** `Record<V, K>` — A new object with values as keys and original keys as values
|
|
442
453
|
|
|
@@ -467,6 +478,7 @@ LABEL_TO_CODE['OK']; // => '200'
|
|
|
467
478
|
### `isEmpty`
|
|
468
479
|
|
|
469
480
|
Checks if a plain object has no own enumerable string-keyed properties.
|
|
481
|
+
`null` and `undefined` are treated as empty objects and return `true`.
|
|
470
482
|
|
|
471
483
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
472
484
|
separately if symbol keys matter for your use case.
|
|
@@ -474,14 +486,14 @@ separately if symbol keys matter for your use case.
|
|
|
474
486
|
```typescript
|
|
475
487
|
import { isEmpty } from '@helpers4/object';
|
|
476
488
|
|
|
477
|
-
isEmpty(value: Record<PropertyKey, unknown>): boolean
|
|
489
|
+
isEmpty(value: Record<PropertyKey, unknown> | null | undefined): boolean
|
|
478
490
|
```
|
|
479
491
|
|
|
480
492
|
**Parameters:**
|
|
481
493
|
|
|
482
|
-
- `value: Record<PropertyKey, unknown
|
|
494
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
483
495
|
|
|
484
|
-
**Returns:** `boolean` — `true` if the object has no own enumerable string-keyed properties
|
|
496
|
+
**Returns:** `boolean` — `true` if the object has no own enumerable string-keyed properties, or if `value` is `null`/`undefined`
|
|
485
497
|
|
|
486
498
|
**Examples:**
|
|
487
499
|
|
|
@@ -510,6 +522,7 @@ isEmpty(obj) // => true (only string keys are counted)
|
|
|
510
522
|
### `isNonEmpty`
|
|
511
523
|
|
|
512
524
|
Checks if a plain object has at least one own enumerable string-keyed property.
|
|
525
|
+
`null` and `undefined` are treated as empty objects and return `false`.
|
|
513
526
|
|
|
514
527
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
515
528
|
separately if symbol keys matter for your use case.
|
|
@@ -517,14 +530,15 @@ separately if symbol keys matter for your use case.
|
|
|
517
530
|
```typescript
|
|
518
531
|
import { isNonEmpty } from '@helpers4/object';
|
|
519
532
|
|
|
520
|
-
isNonEmpty(value: Record<PropertyKey, unknown>):
|
|
533
|
+
isNonEmpty(value: Record<PropertyKey, unknown> | null | undefined): value is Record<PropertyKey, unknown>
|
|
521
534
|
```
|
|
522
535
|
|
|
523
536
|
**Parameters:**
|
|
524
537
|
|
|
525
|
-
- `value: Record<PropertyKey, unknown
|
|
538
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
526
539
|
|
|
527
|
-
**Returns:** `
|
|
540
|
+
**Returns:** `value is Record<PropertyKey, unknown>` — `true` if the object has at least one own enumerable string-keyed property; `false` for empty, `null`, or `undefined`.
|
|
541
|
+
Acts as a type guard: the `if` branch narrows `Record<PropertyKey, unknown> | null | undefined` to `Record<PropertyKey, unknown>`.
|
|
528
542
|
|
|
529
543
|
**Examples:**
|
|
530
544
|
|
|
@@ -565,17 +579,18 @@ When `mapValue` is omitted the original values are preserved;
|
|
|
565
579
|
when `mapKey` is omitted the original keys are preserved.
|
|
566
580
|
|
|
567
581
|
Note: if two different keys map to the same output key the last one wins
|
|
568
|
-
(insertion order).
|
|
582
|
+
(insertion order). Entries whose mapped key is a prototype-polluting string
|
|
583
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
569
584
|
|
|
570
585
|
```typescript
|
|
571
586
|
import { map } from '@helpers4/object';
|
|
572
587
|
|
|
573
|
-
map<TObj extends Record<string, unknown>, TVal = indexedAccess, TKey extends PropertyKey = keyof TObj>(obj: TObj, mapValue?: function, mapKey?: function): Record<TKey, TVal>
|
|
588
|
+
map<TObj extends Record<string, unknown>, TVal = indexedAccess, TKey extends PropertyKey = keyof TObj>(obj: TObj | null | undefined, mapValue?: function, mapKey?: function): Record<TKey, TVal>
|
|
574
589
|
```
|
|
575
590
|
|
|
576
591
|
**Parameters:**
|
|
577
592
|
|
|
578
|
-
- `obj: TObj` — The source object
|
|
593
|
+
- `obj: TObj | null | undefined` — The source object
|
|
579
594
|
- `mapValue?: function` — Callback called with `(value, key)` for each entry.
|
|
580
595
|
Defaults to identity.
|
|
581
596
|
- `mapKey?: function` — Callback called with `(key, value)` for each entry.
|
|
@@ -618,6 +633,67 @@ map(
|
|
|
618
633
|
|
|
619
634
|
---
|
|
620
635
|
|
|
636
|
+
### `mergeDeep`
|
|
637
|
+
|
|
638
|
+
Merges two or more objects deeply, returning a **new** object without mutating any input.
|
|
639
|
+
|
|
640
|
+
Sources are applied left to right. For each key:
|
|
641
|
+
- If both sides are plain objects → merged recursively into a new object.
|
|
642
|
+
- Otherwise → **later source wins** (the earlier value is overwritten).
|
|
643
|
+
- `undefined` in a source **never overwrites** an existing value.
|
|
644
|
+
- `null` in a source **does overwrite** and prevents a later source from
|
|
645
|
+
deep-merging into that key: `mergeDeep({ a: { x: 1 } }, { a: null }, { a: { y: 2 } })`
|
|
646
|
+
→ `{ a: { y: 2 } }` — `x: 1` is permanently lost.
|
|
647
|
+
- Arrays, class instances, and all non-plain-object values are **replaced**, not merged.
|
|
648
|
+
|
|
649
|
+
Own enumerable string and symbol keys of each source are processed at the top level;
|
|
650
|
+
inherited and non-enumerable properties are skipped. Prototype-polluting keys
|
|
651
|
+
(`__proto__`, `constructor`, `prototype`) are silently ignored.
|
|
652
|
+
**Note:** symbol keys inside nested plain-object values are not preserved — they are
|
|
653
|
+
lost when those values are deep-cloned. Only top-level symbol keys survive.
|
|
654
|
+
|
|
655
|
+
**TypeScript return type — intersection semantics**
|
|
656
|
+
|
|
657
|
+
The return type is `A & B & C …` (intersection of all source types). This is accurate
|
|
658
|
+
when keys are disjoint or share the same type:
|
|
659
|
+
```ts
|
|
660
|
+
mergeDeep({ a: 1 }, { b: 'x' }) // { a: number } & { b: string } ✓
|
|
661
|
+
mergeDeep({ a: { b: 1 } }, { a: { c: 2 } }) // { a: { b: number } & { c: number } } ✓
|
|
662
|
+
```
|
|
663
|
+
When the same key carries **incompatible types** across sources, the intersection
|
|
664
|
+
resolves to `never` for that key — TypeScript surfaces the conflict rather than
|
|
665
|
+
silently picking a type:
|
|
666
|
+
```ts
|
|
667
|
+
mergeDeep({ a: 1 }, { a: 'x' }) // { a: never } ← type conflict detected
|
|
668
|
+
```
|
|
669
|
+
At runtime the later source always wins (`'x'`), but the `never` type signals that
|
|
670
|
+
the caller should align their source types. If intentional, cast the result.
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
import { mergeDeep } from '@helpers4/object';
|
|
674
|
+
|
|
675
|
+
mergeDeep<T extends [object, rest]>(sources: [rest]): MergeResult<T>
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
**Parameters:**
|
|
679
|
+
|
|
680
|
+
- `sources: [rest]` — Two or more objects to merge (none are mutated)
|
|
681
|
+
|
|
682
|
+
**Returns:** `MergeResult<T>` — A new object that is the deep merge of all sources
|
|
683
|
+
|
|
684
|
+
**Examples:**
|
|
685
|
+
|
|
686
|
+
*Merge two objects deeply*
|
|
687
|
+
|
|
688
|
+
Returns a new object — neither input is mutated.
|
|
689
|
+
|
|
690
|
+
```typescript
|
|
691
|
+
mergeDeep({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
692
|
+
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
---
|
|
696
|
+
|
|
621
697
|
### `omit`
|
|
622
698
|
|
|
623
699
|
Creates a new object without the specified keys.
|
|
@@ -868,31 +944,72 @@ safeJsonParse('invalid', [])
|
|
|
868
944
|
|
|
869
945
|
### `set`
|
|
870
946
|
|
|
871
|
-
Sets a value in an object
|
|
947
|
+
Sets a value in an object at the given path, creating intermediate objects as needed.
|
|
948
|
+
|
|
949
|
+
**Two path forms are supported:**
|
|
950
|
+
|
|
951
|
+
1. **String path** — dot notation and bracket notation, mixed freely.
|
|
952
|
+
- Dot segments are always string keys: `'layers.1.name'` → keys `['layers', '1', 'name']`.
|
|
953
|
+
- Bracket segments are always number keys: `'layers[1].name'` → keys `['layers', 1, 'name']`.
|
|
954
|
+
- String literal paths give **full compile-time type inference** on the return type.
|
|
955
|
+
- Dynamic (non-literal) strings return `T` (same object type).
|
|
956
|
+
|
|
957
|
+
2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
958
|
+
no parsing performed. Enables symbol-keyed access and precise return-type inference.
|
|
959
|
+
|
|
960
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
961
|
+
Symbol keys are only reachable via the key-array form.
|
|
962
|
+
|
|
963
|
+
Intermediate nodes that are absent, `null`, or not an object are replaced with `{}`.
|
|
964
|
+
Any path containing a string segment equal to `__proto__`, `constructor`, or `prototype`
|
|
965
|
+
is rejected and the original object is returned unchanged (prototype-pollution guard).
|
|
872
966
|
|
|
873
967
|
```typescript
|
|
874
968
|
import { set } from '@helpers4/object';
|
|
875
969
|
|
|
876
|
-
set(obj:
|
|
970
|
+
set<T extends object, P extends string | readonly PropertyKey[], V extends unknown>(obj: T, path: P, value: V): conditional
|
|
877
971
|
```
|
|
878
972
|
|
|
879
973
|
**Parameters:**
|
|
880
974
|
|
|
881
|
-
- `obj:
|
|
882
|
-
- `path:
|
|
883
|
-
- `value:
|
|
975
|
+
- `obj: T` — The object to mutate
|
|
976
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
977
|
+
- `value: V` — Value to assign at the path
|
|
884
978
|
|
|
885
|
-
**Returns:** `
|
|
979
|
+
**Returns:** `conditional` — The mutated object (same reference, narrowed type)
|
|
886
980
|
|
|
887
981
|
**Examples:**
|
|
888
982
|
|
|
889
|
-
*Set a nested property*
|
|
983
|
+
*Set a nested property (dot notation)*
|
|
890
984
|
|
|
891
|
-
Creates intermediate objects as needed
|
|
985
|
+
Creates intermediate objects as needed. All segments are string keys — including numeric-looking ones like "1".
|
|
892
986
|
|
|
893
987
|
```typescript
|
|
894
988
|
set({}, 'a.b.c', 42)
|
|
895
989
|
// => { a: { b: { c: 42 } } }
|
|
990
|
+
|
|
991
|
+
set({}, 'layers.1.name', 'bg')
|
|
992
|
+
// => { layers: { '1': { name: 'bg' } } } // '1' is a string key
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
*Set via bracket notation*
|
|
996
|
+
|
|
997
|
+
Square-bracket indices become numeric keys. Useful when the path targets an array element.
|
|
998
|
+
|
|
999
|
+
```typescript
|
|
1000
|
+
const obj = { layers: [{}, { name: 'old' }] }
|
|
1001
|
+
set(obj, 'layers[1].name', 'new')
|
|
1002
|
+
// => { layers: [{}, { name: 'new' }] }
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
*Set via key array (supports symbols)*
|
|
1006
|
+
|
|
1007
|
+
Pass an explicit PropertyKey[] to bypass parsing. Supports string, number, and symbol keys.
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
const id = Symbol('id')
|
|
1011
|
+
set({}, ['user', id], 'alice')
|
|
1012
|
+
// => { user: { [id]: 'alice' } }
|
|
896
1013
|
```
|
|
897
1014
|
|
|
898
1015
|
---
|