@helpers4/object 2.0.4 → 3.0.0-alpha.1
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 +5 -3
- package/lib/index.d.ts +243 -78
- package/lib/index.js +121 -69
- package/lib/index.js.map +1 -1
- package/llms.txt +179 -151
- package/meta/api.json +150 -202
- 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:
|
|
4
|
+
> Package: `@helpers4/object` — Version: 3.0.0-alpha.1
|
|
5
5
|
> License: LGPL-3.0-or-later
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
@@ -15,38 +15,79 @@ 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
|
-
| `pick` | Creates a new object with only the specified keys. |
|
|
38
|
+
| `pick` | Creates a new object with only the specified keys. Keys that are prototype-polluting strings (`__pro |
|
|
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.
|
|
89
|
+
Own keys that are prototype-polluting strings (`__proto__`, `constructor`, `prototype`) are
|
|
90
|
+
silently skipped.
|
|
50
91
|
|
|
51
92
|
```typescript
|
|
52
93
|
import { compact } from '@helpers4/object';
|
|
@@ -106,98 +147,6 @@ compact({ name: 'Alice', email: '', age: 0, role: 'admin' })
|
|
|
106
147
|
|
|
107
148
|
---
|
|
108
149
|
|
|
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
150
|
### `diff`
|
|
202
151
|
|
|
203
152
|
Structural object diff.
|
|
@@ -348,40 +297,59 @@ Gets a value from an object using a dot/bracket-notated path or explicit key arr
|
|
|
348
297
|
**Two path forms are supported:**
|
|
349
298
|
|
|
350
299
|
1. **String path** — dot notation (`'a.b.c'`) and bracket notation (`'layers[1].name'`)
|
|
351
|
-
are both accepted and mixed freely.
|
|
352
|
-
|
|
300
|
+
are both accepted and mixed freely.
|
|
301
|
+
- Dot segments are always string keys: `'a.1'` → key `'1'` (string).
|
|
302
|
+
- Bracket segments are always number keys: `'a[1]'` → key `1` (number).
|
|
303
|
+
- String literal paths give **full compile-time type inference** on the return type.
|
|
304
|
+
- Dynamic (non-literal) strings return `unknown`.
|
|
353
305
|
|
|
354
306
|
2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
355
307
|
no parsing performed. Enables symbol-keyed traversal and compile-time type inference:
|
|
356
308
|
`get(obj, ['a', 'b'] as const)` infers the return type from the path.
|
|
357
309
|
|
|
310
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
311
|
+
Symbol keys are only reachable via the key-array form.
|
|
312
|
+
|
|
358
313
|
```typescript
|
|
359
314
|
import { get } from '@helpers4/object';
|
|
360
315
|
|
|
361
|
-
get<
|
|
316
|
+
get<D>(obj: null | undefined, path: string | readonly PropertyKey[], defaultValue: D): D
|
|
362
317
|
```
|
|
363
318
|
|
|
364
319
|
**Parameters:**
|
|
365
320
|
|
|
366
|
-
- `obj:
|
|
367
|
-
- `path: string` — Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
368
|
-
- `defaultValue
|
|
321
|
+
- `obj: null | undefined` — The object to read from
|
|
322
|
+
- `path: string | readonly PropertyKey[]` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
323
|
+
- `defaultValue: D` — Returned when the path is absent or resolves to `undefined`
|
|
369
324
|
|
|
370
|
-
**Returns:** `
|
|
325
|
+
**Returns:** `D` — The value at the path, or `defaultValue`
|
|
371
326
|
|
|
372
327
|
```typescript
|
|
373
328
|
import { get } from '@helpers4/object';
|
|
374
329
|
|
|
375
|
-
get
|
|
330
|
+
get(obj: null | undefined, path: string | readonly PropertyKey[]): undefined
|
|
376
331
|
```
|
|
377
332
|
|
|
378
333
|
**Parameters:**
|
|
379
334
|
|
|
380
|
-
- `obj:
|
|
381
|
-
- `path:
|
|
382
|
-
|
|
335
|
+
- `obj: null | undefined` — The object to read from
|
|
336
|
+
- `path: string | readonly PropertyKey[]` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
337
|
+
|
|
338
|
+
**Returns:** `undefined` — The value at the path, or `defaultValue`
|
|
383
339
|
|
|
384
|
-
|
|
340
|
+
```typescript
|
|
341
|
+
import { get } from '@helpers4/object';
|
|
342
|
+
|
|
343
|
+
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
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Parameters:**
|
|
347
|
+
|
|
348
|
+
- `obj: T | null | undefined` — The object to read from
|
|
349
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
350
|
+
- `defaultValue?: DeepGet<T, ParsePath<P>>` — Returned when the path is absent or resolves to `undefined`
|
|
351
|
+
|
|
352
|
+
**Returns:** `DeepGet<T, ParsePath<P>> | undefined` — The value at the path, or `defaultValue`
|
|
385
353
|
|
|
386
354
|
**Examples:**
|
|
387
355
|
|
|
@@ -421,16 +389,19 @@ Groups an array of items by a key derived from each item.
|
|
|
421
389
|
|
|
422
390
|
A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
423
391
|
older targets and provides stricter return-type inference.
|
|
392
|
+
`null` and `undefined` are treated as empty arrays and return `{}`.
|
|
393
|
+
Items whose computed key is a prototype-polluting string (`__proto__`,
|
|
394
|
+
`constructor`, `prototype`) are silently skipped.
|
|
424
395
|
|
|
425
396
|
```typescript
|
|
426
397
|
import { groupBy } from '@helpers4/object';
|
|
427
398
|
|
|
428
|
-
groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: function): Partial<Record<K, T[]>>
|
|
399
|
+
groupBy<T, K extends PropertyKey>(items: readonly T[] | null | undefined, keyFn: function): Partial<Record<K, T[]>>
|
|
429
400
|
```
|
|
430
401
|
|
|
431
402
|
**Parameters:**
|
|
432
403
|
|
|
433
|
-
- `items: readonly T[]` — The array to group
|
|
404
|
+
- `items: readonly T[] | null | undefined` — The array to group
|
|
434
405
|
- `keyFn: function` — Function that returns the group key for each item
|
|
435
406
|
|
|
436
407
|
**Returns:** `Partial<Record<K, T[]>>` — A record mapping each key to the array of items with that key
|
|
@@ -466,16 +437,19 @@ groupBy(users, u => u.role)
|
|
|
466
437
|
|
|
467
438
|
Returns a new object with keys and values swapped.
|
|
468
439
|
If multiple keys share the same value, the last one wins.
|
|
440
|
+
`null` and `undefined` are treated as empty objects and return `{}`.
|
|
441
|
+
Entries whose source key **or** value is a prototype-polluting string
|
|
442
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
469
443
|
|
|
470
444
|
```typescript
|
|
471
445
|
import { invert } from '@helpers4/object';
|
|
472
446
|
|
|
473
|
-
invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>
|
|
447
|
+
invert<K extends string, V extends PropertyKey>(obj: Record<K, V> | null | undefined): Record<V, K>
|
|
474
448
|
```
|
|
475
449
|
|
|
476
450
|
**Parameters:**
|
|
477
451
|
|
|
478
|
-
- `obj: Record<K, V
|
|
452
|
+
- `obj: Record<K, V> | null | undefined` — The object whose keys and values are to be swapped
|
|
479
453
|
|
|
480
454
|
**Returns:** `Record<V, K>` — A new object with values as keys and original keys as values
|
|
481
455
|
|
|
@@ -506,6 +480,7 @@ LABEL_TO_CODE['OK']; // => '200'
|
|
|
506
480
|
### `isEmpty`
|
|
507
481
|
|
|
508
482
|
Checks if a plain object has no own enumerable string-keyed properties.
|
|
483
|
+
`null` and `undefined` are treated as empty objects and return `true`.
|
|
509
484
|
|
|
510
485
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
511
486
|
separately if symbol keys matter for your use case.
|
|
@@ -513,14 +488,14 @@ separately if symbol keys matter for your use case.
|
|
|
513
488
|
```typescript
|
|
514
489
|
import { isEmpty } from '@helpers4/object';
|
|
515
490
|
|
|
516
|
-
isEmpty(value: Record<PropertyKey, unknown>): boolean
|
|
491
|
+
isEmpty(value: Record<PropertyKey, unknown> | null | undefined): boolean
|
|
517
492
|
```
|
|
518
493
|
|
|
519
494
|
**Parameters:**
|
|
520
495
|
|
|
521
|
-
- `value: Record<PropertyKey, unknown
|
|
496
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
522
497
|
|
|
523
|
-
**Returns:** `boolean` — `true` if the object has no own enumerable string-keyed properties
|
|
498
|
+
**Returns:** `boolean` — `true` if the object has no own enumerable string-keyed properties, or if `value` is `null`/`undefined`
|
|
524
499
|
|
|
525
500
|
**Examples:**
|
|
526
501
|
|
|
@@ -549,6 +524,7 @@ isEmpty(obj) // => true (only string keys are counted)
|
|
|
549
524
|
### `isNonEmpty`
|
|
550
525
|
|
|
551
526
|
Checks if a plain object has at least one own enumerable string-keyed property.
|
|
527
|
+
`null` and `undefined` are treated as empty objects and return `false`.
|
|
552
528
|
|
|
553
529
|
Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
554
530
|
separately if symbol keys matter for your use case.
|
|
@@ -556,14 +532,15 @@ separately if symbol keys matter for your use case.
|
|
|
556
532
|
```typescript
|
|
557
533
|
import { isNonEmpty } from '@helpers4/object';
|
|
558
534
|
|
|
559
|
-
isNonEmpty(value: Record<PropertyKey, unknown>):
|
|
535
|
+
isNonEmpty(value: Record<PropertyKey, unknown> | null | undefined): value is Record<PropertyKey, unknown>
|
|
560
536
|
```
|
|
561
537
|
|
|
562
538
|
**Parameters:**
|
|
563
539
|
|
|
564
|
-
- `value: Record<PropertyKey, unknown
|
|
540
|
+
- `value: Record<PropertyKey, unknown> | null | undefined` — The object to check
|
|
565
541
|
|
|
566
|
-
**Returns:** `
|
|
542
|
+
**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`.
|
|
543
|
+
Acts as a type guard: the `if` branch narrows `Record<PropertyKey, unknown> | null | undefined` to `Record<PropertyKey, unknown>`.
|
|
567
544
|
|
|
568
545
|
**Examples:**
|
|
569
546
|
|
|
@@ -604,17 +581,18 @@ When `mapValue` is omitted the original values are preserved;
|
|
|
604
581
|
when `mapKey` is omitted the original keys are preserved.
|
|
605
582
|
|
|
606
583
|
Note: if two different keys map to the same output key the last one wins
|
|
607
|
-
(insertion order).
|
|
584
|
+
(insertion order). Entries whose mapped key is a prototype-polluting string
|
|
585
|
+
(`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
608
586
|
|
|
609
587
|
```typescript
|
|
610
588
|
import { map } from '@helpers4/object';
|
|
611
589
|
|
|
612
|
-
map<TObj extends Record<string, unknown>, TVal = indexedAccess, TKey extends PropertyKey = keyof TObj>(obj: TObj, mapValue?: function, mapKey?: function): Record<TKey, TVal>
|
|
590
|
+
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
591
|
```
|
|
614
592
|
|
|
615
593
|
**Parameters:**
|
|
616
594
|
|
|
617
|
-
- `obj: TObj` — The source object
|
|
595
|
+
- `obj: TObj | null | undefined` — The source object
|
|
618
596
|
- `mapValue?: function` — Callback called with `(value, key)` for each entry.
|
|
619
597
|
Defaults to identity.
|
|
620
598
|
- `mapKey?: function` — Callback called with `(key, value)` for each entry.
|
|
@@ -657,6 +635,67 @@ map(
|
|
|
657
635
|
|
|
658
636
|
---
|
|
659
637
|
|
|
638
|
+
### `mergeDeep`
|
|
639
|
+
|
|
640
|
+
Merges two or more objects deeply, returning a **new** object without mutating any input.
|
|
641
|
+
|
|
642
|
+
Sources are applied left to right. For each key:
|
|
643
|
+
- If both sides are plain objects → merged recursively into a new object.
|
|
644
|
+
- Otherwise → **later source wins** (the earlier value is overwritten).
|
|
645
|
+
- `undefined` in a source **never overwrites** an existing value.
|
|
646
|
+
- `null` in a source **does overwrite** and prevents a later source from
|
|
647
|
+
deep-merging into that key: `mergeDeep({ a: { x: 1 } }, { a: null }, { a: { y: 2 } })`
|
|
648
|
+
→ `{ a: { y: 2 } }` — `x: 1` is permanently lost.
|
|
649
|
+
- Arrays, class instances, and all non-plain-object values are **replaced**, not merged.
|
|
650
|
+
|
|
651
|
+
Own enumerable string and symbol keys of each source are processed at the top level;
|
|
652
|
+
inherited and non-enumerable properties are skipped. Prototype-polluting keys
|
|
653
|
+
(`__proto__`, `constructor`, `prototype`) are silently ignored.
|
|
654
|
+
**Note:** symbol keys inside nested plain-object values are not preserved — they are
|
|
655
|
+
lost when those values are deep-cloned. Only top-level symbol keys survive.
|
|
656
|
+
|
|
657
|
+
**TypeScript return type — intersection semantics**
|
|
658
|
+
|
|
659
|
+
The return type is `A & B & C …` (intersection of all source types). This is accurate
|
|
660
|
+
when keys are disjoint or share the same type:
|
|
661
|
+
```ts
|
|
662
|
+
mergeDeep({ a: 1 }, { b: 'x' }) // { a: number } & { b: string } ✓
|
|
663
|
+
mergeDeep({ a: { b: 1 } }, { a: { c: 2 } }) // { a: { b: number } & { c: number } } ✓
|
|
664
|
+
```
|
|
665
|
+
When the same key carries **incompatible types** across sources, the intersection
|
|
666
|
+
resolves to `never` for that key — TypeScript surfaces the conflict rather than
|
|
667
|
+
silently picking a type:
|
|
668
|
+
```ts
|
|
669
|
+
mergeDeep({ a: 1 }, { a: 'x' }) // { a: never } ← type conflict detected
|
|
670
|
+
```
|
|
671
|
+
At runtime the later source always wins (`'x'`), but the `never` type signals that
|
|
672
|
+
the caller should align their source types. If intentional, cast the result.
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { mergeDeep } from '@helpers4/object';
|
|
676
|
+
|
|
677
|
+
mergeDeep<T extends [object, rest]>(sources: [rest]): MergeResult<T>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**Parameters:**
|
|
681
|
+
|
|
682
|
+
- `sources: [rest]` — Two or more objects to merge (none are mutated)
|
|
683
|
+
|
|
684
|
+
**Returns:** `MergeResult<T>` — A new object that is the deep merge of all sources
|
|
685
|
+
|
|
686
|
+
**Examples:**
|
|
687
|
+
|
|
688
|
+
*Merge two objects deeply*
|
|
689
|
+
|
|
690
|
+
Returns a new object — neither input is mutated.
|
|
691
|
+
|
|
692
|
+
```typescript
|
|
693
|
+
mergeDeep({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
694
|
+
// => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
660
699
|
### `omit`
|
|
661
700
|
|
|
662
701
|
Creates a new object without the specified keys.
|
|
@@ -726,6 +765,8 @@ omit(user, ['password', 'token'])
|
|
|
726
765
|
### `pick`
|
|
727
766
|
|
|
728
767
|
Creates a new object with only the specified keys.
|
|
768
|
+
Keys that are prototype-polluting strings (`__proto__`, `constructor`, `prototype`) are
|
|
769
|
+
silently skipped.
|
|
729
770
|
|
|
730
771
|
```typescript
|
|
731
772
|
import { pick } from '@helpers4/object';
|
|
@@ -909,18 +950,19 @@ safeJsonParse('invalid', [])
|
|
|
909
950
|
|
|
910
951
|
Sets a value in an object at the given path, creating intermediate objects as needed.
|
|
911
952
|
|
|
912
|
-
**
|
|
953
|
+
**Two path forms are supported:**
|
|
913
954
|
|
|
914
|
-
1. **
|
|
915
|
-
`
|
|
955
|
+
1. **String path** — dot notation and bracket notation, mixed freely.
|
|
956
|
+
- Dot segments are always string keys: `'layers.1.name'` → keys `['layers', '1', 'name']`.
|
|
957
|
+
- Bracket segments are always number keys: `'layers[1].name'` → keys `['layers', 1, 'name']`.
|
|
958
|
+
- String literal paths give **full compile-time type inference** on the return type.
|
|
959
|
+
- Dynamic (non-literal) strings return `T` (same object type).
|
|
916
960
|
|
|
917
|
-
2. **
|
|
918
|
-
|
|
919
|
-
Dot and bracket can be mixed freely: `"a[0].b[2].c"`.
|
|
961
|
+
2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
962
|
+
no parsing performed. Enables symbol-keyed access and precise return-type inference.
|
|
920
963
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
`["layers", 1, Symbol('id')]`.
|
|
964
|
+
Both forms support **all objects** (plain objects, arrays, class instances).
|
|
965
|
+
Symbol keys are only reachable via the key-array form.
|
|
924
966
|
|
|
925
967
|
Intermediate nodes that are absent, `null`, or not an object are replaced with `{}`.
|
|
926
968
|
Any path containing a string segment equal to `__proto__`, `constructor`, or `prototype`
|
|
@@ -929,30 +971,16 @@ is rejected and the original object is returned unchanged (prototype-pollution g
|
|
|
929
971
|
```typescript
|
|
930
972
|
import { set } from '@helpers4/object';
|
|
931
973
|
|
|
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>
|
|
974
|
+
set<T extends object, P extends string | readonly PropertyKey[], V extends unknown>(obj: T, path: P, value: V): conditional
|
|
947
975
|
```
|
|
948
976
|
|
|
949
977
|
**Parameters:**
|
|
950
978
|
|
|
951
979
|
- `obj: T` — The object to mutate
|
|
952
|
-
- `path:
|
|
980
|
+
- `path: P` — Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
953
981
|
- `value: V` — Value to assign at the path
|
|
954
982
|
|
|
955
|
-
**Returns:** `
|
|
983
|
+
**Returns:** `conditional` — The mutated object (same reference, narrowed type)
|
|
956
984
|
|
|
957
985
|
**Examples:**
|
|
958
986
|
|