@helpers4/object 2.0.4 → 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 +226 -74
- package/lib/index.js +99 -63
- package/lib/index.js.map +1 -1
- package/llms.txt +174 -150
- package/meta/api.json +145 -197
- package/meta/examples.json +20 -20
- 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. Recursively merges own enumerable properties — both string and s |
|
|
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
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 at the given path, creating intermediate objects as needed. **
|
|
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,98 +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
|
-
Recursively merges own enumerable properties — both string and symbol keys.
|
|
145
|
-
Plain objects are merged recursively; all other values (arrays, class instances,
|
|
146
|
-
primitives, etc.) are replaced by the source value.
|
|
147
|
-
`undefined` source values do not overwrite existing target values.
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
import { deepMerge } from '@helpers4/object';
|
|
151
|
-
|
|
152
|
-
deepMerge<T extends Record<PropertyKey, unknown>>(target: T, sources: Record<PropertyKey, unknown>[]): T
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
**Parameters:**
|
|
156
|
-
|
|
157
|
-
- `target: T` — The target object (mutated in place)
|
|
158
|
-
- `sources: Record<PropertyKey, unknown>[]` — One or more source objects to merge into the target
|
|
159
|
-
|
|
160
|
-
**Returns:** `T` — The mutated target
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import { deepMerge } from '@helpers4/object';
|
|
164
|
-
|
|
165
|
-
deepMerge(target: undefined, sources: Record<PropertyKey, unknown>[]): undefined
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
**Parameters:**
|
|
169
|
-
|
|
170
|
-
- `target: undefined` — The target object (mutated in place)
|
|
171
|
-
- `sources: Record<PropertyKey, unknown>[]` — One or more source objects to merge into the target
|
|
172
|
-
|
|
173
|
-
**Returns:** `undefined` — The mutated target
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
import { deepMerge } from '@helpers4/object';
|
|
177
|
-
|
|
178
|
-
deepMerge(target: null, sources: Record<PropertyKey, unknown>[]): null
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**Parameters:**
|
|
182
|
-
|
|
183
|
-
- `target: null` — The target object (mutated in place)
|
|
184
|
-
- `sources: Record<PropertyKey, unknown>[]` — One or more source objects to merge into the target
|
|
185
|
-
|
|
186
|
-
**Returns:** `null` — The mutated target
|
|
187
|
-
|
|
188
|
-
**Examples:**
|
|
189
|
-
|
|
190
|
-
*Merge two objects deeply*
|
|
191
|
-
|
|
192
|
-
Recursively merges source properties into the target object.
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
196
|
-
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
148
|
### `diff`
|
|
202
149
|
|
|
203
150
|
Structural object diff.
|
|
@@ -348,40 +295,59 @@ Gets a value from an object using a dot/bracket-notated path or explicit key arr
|
|
|
348
295
|
**Two path forms are supported:**
|
|
349
296
|
|
|
350
297
|
1. **String path** — dot notation (`'a.b.c'`) and bracket notation (`'layers[1].name'`)
|
|
351
|
-
are both accepted and mixed freely.
|
|
352
|
-
|
|
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`.
|
|
353
303
|
|
|
354
304
|
2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
355
305
|
no parsing performed. Enables symbol-keyed traversal and compile-time type inference:
|
|
356
306
|
`get(obj, ['a', 'b'] as const)` infers the return type from the path.
|
|
357
307
|
|
|
308
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
309
|
+
Symbol keys are only reachable via the key-array form.
|
|
310
|
+
|
|
358
311
|
```typescript
|
|
359
312
|
import { get } from '@helpers4/object';
|
|
360
313
|
|
|
361
|
-
get<
|
|
314
|
+
get<D>(obj: null | undefined, path: string | readonly PropertyKey[], defaultValue: D): D
|
|
362
315
|
```
|
|
363
316
|
|
|
364
317
|
**Parameters:**
|
|
365
318
|
|
|
366
|
-
- `obj:
|
|
367
|
-
- `path: string` — Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
368
|
-
- `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`
|
|
369
322
|
|
|
370
|
-
**Returns:** `
|
|
323
|
+
**Returns:** `D` — The value at the path, or `defaultValue`
|
|
371
324
|
|
|
372
325
|
```typescript
|
|
373
326
|
import { get } from '@helpers4/object';
|
|
374
327
|
|
|
375
|
-
get
|
|
328
|
+
get(obj: null | undefined, path: string | readonly PropertyKey[]): undefined
|
|
376
329
|
```
|
|
377
330
|
|
|
378
331
|
**Parameters:**
|
|
379
332
|
|
|
380
|
-
- `obj:
|
|
381
|
-
- `path:
|
|
382
|
-
|
|
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`
|
|
383
337
|
|
|
384
|
-
|
|
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`
|
|
385
351
|
|
|
386
352
|
**Examples:**
|
|
387
353
|
|
|
@@ -421,16 +387,19 @@ Groups an array of items by a key derived from each item.
|
|
|
421
387
|
|
|
422
388
|
A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
423
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.
|
|
424
393
|
|
|
425
394
|
```typescript
|
|
426
395
|
import { groupBy } from '@helpers4/object';
|
|
427
396
|
|
|
428
|
-
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[]>>
|
|
429
398
|
```
|
|
430
399
|
|
|
431
400
|
**Parameters:**
|
|
432
401
|
|
|
433
|
-
- `items: readonly T[]` — The array to group
|
|
402
|
+
- `items: readonly T[] | null | undefined` — The array to group
|
|
434
403
|
- `keyFn: function` — Function that returns the group key for each item
|
|
435
404
|
|
|
436
405
|
**Returns:** `Partial<Record<K, T[]>>` — A record mapping each key to the array of items with that key
|
|
@@ -466,16 +435,19 @@ groupBy(users, u => u.role)
|
|
|
466
435
|
|
|
467
436
|
Returns a new object with keys and values swapped.
|
|
468
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.
|
|
469
441
|
|
|
470
442
|
```typescript
|
|
471
443
|
import { invert } from '@helpers4/object';
|
|
472
444
|
|
|
473
|
-
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>
|
|
474
446
|
```
|
|
475
447
|
|
|
476
448
|
**Parameters:**
|
|
477
449
|
|
|
478
|
-
- `obj: Record<K, V
|
|
450
|
+
- `obj: Record<K, V> | null | undefined` — The object whose keys and values are to be swapped
|
|
479
451
|
|
|
480
452
|
**Returns:** `Record<V, K>` — A new object with values as keys and original keys as values
|
|
481
453
|
|
|
@@ -506,6 +478,7 @@ LABEL_TO_CODE['OK']; // => '200'
|
|
|
506
478
|
### `isEmpty`
|
|
507
479
|
|
|
508
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`.
|
|
509
482
|
|
|
510
483
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
511
484
|
separately if symbol keys matter for your use case.
|
|
@@ -513,14 +486,14 @@ separately if symbol keys matter for your use case.
|
|
|
513
486
|
```typescript
|
|
514
487
|
import { isEmpty } from '@helpers4/object';
|
|
515
488
|
|
|
516
|
-
isEmpty(value: Record<PropertyKey, unknown>): boolean
|
|
489
|
+
isEmpty(value: Record<PropertyKey, unknown> | null | undefined): boolean
|
|
517
490
|
```
|
|
518
491
|
|
|
519
492
|
**Parameters:**
|
|
520
493
|
|
|
521
|
-
- `value: Record<PropertyKey, unknown
|
|
494
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
522
495
|
|
|
523
|
-
**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`
|
|
524
497
|
|
|
525
498
|
**Examples:**
|
|
526
499
|
|
|
@@ -549,6 +522,7 @@ isEmpty(obj) // => true (only string keys are counted)
|
|
|
549
522
|
### `isNonEmpty`
|
|
550
523
|
|
|
551
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`.
|
|
552
526
|
|
|
553
527
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
554
528
|
separately if symbol keys matter for your use case.
|
|
@@ -556,14 +530,15 @@ separately if symbol keys matter for your use case.
|
|
|
556
530
|
```typescript
|
|
557
531
|
import { isNonEmpty } from '@helpers4/object';
|
|
558
532
|
|
|
559
|
-
isNonEmpty(value: Record<PropertyKey, unknown>):
|
|
533
|
+
isNonEmpty(value: Record<PropertyKey, unknown> | null | undefined): value is Record<PropertyKey, unknown>
|
|
560
534
|
```
|
|
561
535
|
|
|
562
536
|
**Parameters:**
|
|
563
537
|
|
|
564
|
-
- `value: Record<PropertyKey, unknown
|
|
538
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
565
539
|
|
|
566
|
-
**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>`.
|
|
567
542
|
|
|
568
543
|
**Examples:**
|
|
569
544
|
|
|
@@ -604,17 +579,18 @@ When `mapValue` is omitted the original values are preserved;
|
|
|
604
579
|
when `mapKey` is omitted the original keys are preserved.
|
|
605
580
|
|
|
606
581
|
Note: if two different keys map to the same output key the last one wins
|
|
607
|
-
(insertion order).
|
|
582
|
+
(insertion order). Entries whose mapped key is a prototype-polluting string
|
|
583
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
608
584
|
|
|
609
585
|
```typescript
|
|
610
586
|
import { map } from '@helpers4/object';
|
|
611
587
|
|
|
612
|
-
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>
|
|
613
589
|
```
|
|
614
590
|
|
|
615
591
|
**Parameters:**
|
|
616
592
|
|
|
617
|
-
- `obj: TObj` — The source object
|
|
593
|
+
- `obj: TObj | null | undefined` — The source object
|
|
618
594
|
- `mapValue?: function` — Callback called with `(value, key)` for each entry.
|
|
619
595
|
Defaults to identity.
|
|
620
596
|
- `mapKey?: function` — Callback called with `(key, value)` for each entry.
|
|
@@ -657,6 +633,67 @@ map(
|
|
|
657
633
|
|
|
658
634
|
---
|
|
659
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
|
+
|
|
660
697
|
### `omit`
|
|
661
698
|
|
|
662
699
|
Creates a new object without the specified keys.
|
|
@@ -909,18 +946,19 @@ safeJsonParse('invalid', [])
|
|
|
909
946
|
|
|
910
947
|
Sets a value in an object at the given path, creating intermediate objects as needed.
|
|
911
948
|
|
|
912
|
-
**
|
|
949
|
+
**Two path forms are supported:**
|
|
913
950
|
|
|
914
|
-
1. **
|
|
915
|
-
`
|
|
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).
|
|
916
956
|
|
|
917
|
-
2. **
|
|
918
|
-
|
|
919
|
-
Dot and bracket can be mixed freely: `"a[0].b[2].c"`.
|
|
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.
|
|
920
959
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
`["layers", 1, Symbol('id')]`.
|
|
960
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
961
|
+
Symbol keys are only reachable via the key-array form.
|
|
924
962
|
|
|
925
963
|
Intermediate nodes that are absent, `null`, or not an object are replaced with `{}`.
|
|
926
964
|
Any path containing a string segment equal to `__proto__`, `constructor`, or `prototype`
|
|
@@ -929,30 +967,16 @@ is rejected and the original object is returned unchanged (prototype-pollution g
|
|
|
929
967
|
```typescript
|
|
930
968
|
import { set } from '@helpers4/object';
|
|
931
969
|
|
|
932
|
-
set(obj:
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
**Parameters:**
|
|
936
|
-
|
|
937
|
-
- `obj: Record<string, unknown>` — The object to mutate
|
|
938
|
-
- `path: string` — Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
939
|
-
- `value: unknown` — Value to assign at the path
|
|
940
|
-
|
|
941
|
-
**Returns:** `Record<string, unknown>` — The mutated object (same reference)
|
|
942
|
-
|
|
943
|
-
```typescript
|
|
944
|
-
import { set } from '@helpers4/object';
|
|
945
|
-
|
|
946
|
-
set<T extends object, Path extends readonly PropertyKey[], V extends unknown>(obj: T, path: Path, value: V): DeepSet<T, Path, V>
|
|
970
|
+
set<T extends object, P extends string | readonly PropertyKey[], V extends unknown>(obj: T, path: P, value: V): conditional
|
|
947
971
|
```
|
|
948
972
|
|
|
949
973
|
**Parameters:**
|
|
950
974
|
|
|
951
975
|
- `obj: T` — The object to mutate
|
|
952
|
-
- `path:
|
|
976
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
953
977
|
- `value: V` — Value to assign at the path
|
|
954
978
|
|
|
955
|
-
**Returns:** `
|
|
979
|
+
**Returns:** `conditional` — The mutated object (same reference, narrowed type)
|
|
956
980
|
|
|
957
981
|
**Examples:**
|
|
958
982
|
|