@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/README.md
CHANGED
|
@@ -12,9 +12,8 @@ A set of helpers for working with URLs.
|
|
|
12
12
|
[https://helpers4.dev/typescript/categories/object/](https://helpers4.dev/typescript/categories/object/)
|
|
13
13
|
|
|
14
14
|
<!-- AUTOMATIC-METHODS -->
|
|
15
|
+
- cloneDeep
|
|
15
16
|
- compact
|
|
16
|
-
- deepClone
|
|
17
|
-
- deepMerge
|
|
18
17
|
- diff
|
|
19
18
|
- equalsDeep
|
|
20
19
|
- equalsShallow
|
|
@@ -24,6 +23,7 @@ A set of helpers for working with URLs.
|
|
|
24
23
|
- isEmpty
|
|
25
24
|
- isNonEmpty
|
|
26
25
|
- map
|
|
26
|
+
- mergeDeep
|
|
27
27
|
- omit
|
|
28
28
|
- pick
|
|
29
29
|
- removeUndefinedNull
|
|
@@ -39,6 +39,7 @@ A set of helpers for working with URLs.
|
|
|
39
39
|
- [commit](../commit)
|
|
40
40
|
- [date](../date)
|
|
41
41
|
- [function](../function)
|
|
42
|
+
- [guard](../guard)
|
|
42
43
|
- [id](../id)
|
|
43
44
|
- [markdown](../markdown)
|
|
44
45
|
- [node](../node)
|
|
@@ -61,6 +62,7 @@ A set of helpers for working with URLs.
|
|
|
61
62
|
| commit | [@helpers4/commit](https://www.npmjs.com/package/@helpers4/commit) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/commit) | Conventional Commits parsing and analysis |
|
|
62
63
|
| date | [@helpers4/date](https://www.npmjs.com/package/@helpers4/date) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/date) | Date and time utility functions |
|
|
63
64
|
| function | [@helpers4/function](https://www.npmjs.com/package/@helpers4/function) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/function) | Function utilities and type guards |
|
|
65
|
+
| guard | [@helpers4/guard](https://www.npmjs.com/package/@helpers4/guard) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/guard) | Runtime type guard utilities |
|
|
64
66
|
| id | [@helpers4/id](https://www.npmjs.com/package/@helpers4/id) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/id) | Unique identifier generation utilities |
|
|
65
67
|
| markdown | [@helpers4/markdown](https://www.npmjs.com/package/@helpers4/markdown) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/markdown) | Markdown formatting and escaping utilities |
|
|
66
68
|
| node | [@helpers4/node](https://www.npmjs.com/package/@helpers4/node) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/node) | Node.js-specific utilities |
|
|
@@ -69,7 +71,7 @@ A set of helpers for working with URLs.
|
|
|
69
71
|
| observable | [@helpers4/observable](https://www.npmjs.com/package/@helpers4/observable) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/observable) | Observable utilities and combinators |
|
|
70
72
|
| promise | [@helpers4/promise](https://www.npmjs.com/package/@helpers4/promise) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/promise) | Promise utilities and error handling |
|
|
71
73
|
| string | [@helpers4/string](https://www.npmjs.com/package/@helpers4/string) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/string) | String manipulation and formatting utilities |
|
|
72
|
-
| type | [@helpers4/type](https://www.npmjs.com/package/@helpers4/type) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/type) |
|
|
74
|
+
| type | [@helpers4/type](https://www.npmjs.com/package/@helpers4/type) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/type) | Compile-time TypeScript utility types |
|
|
73
75
|
| url | [@helpers4/url](https://www.npmjs.com/package/@helpers4/url) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/url) | URL parsing and manipulation utilities |
|
|
74
76
|
| version | [@helpers4/version](https://www.npmjs.com/package/@helpers4/version) | [Source](https://github.com/helpers4/helpers4/tree/main/helpers/version) | Version string manipulation utilities |
|
|
75
77
|
<!-- /AUTOMATIC-CATEGORIES-TABLE -->
|
package/lib/index.d.ts
CHANGED
|
@@ -4,30 +4,31 @@
|
|
|
4
4
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Creates a deep copy of an object or array.
|
|
8
|
+
*
|
|
9
|
+
* Recursively clones own enumerable string keys. `Date` instances are
|
|
10
|
+
* reconstructed with the same epoch value. Prototype-polluting keys
|
|
11
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
12
|
+
* Does **not** handle circular references.
|
|
13
|
+
*
|
|
14
|
+
* **Limitation:** symbol-keyed own properties are **not** copied — only string
|
|
15
|
+
* keys are processed. Use `structuredClone` if symbol propagation is required.
|
|
16
|
+
*
|
|
17
|
+
* @param obj - The value to clone
|
|
18
|
+
* @returns A deep copy of `obj`
|
|
10
19
|
* @example
|
|
11
|
-
*
|
|
12
|
-
* // => { a: 1,
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* This file is part of helpers4.
|
|
21
|
-
* Copyright (C) 2025 baxyz
|
|
22
|
-
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* Creates a deep copy of an object or array
|
|
26
|
-
* @param obj - The object to clone
|
|
27
|
-
* @returns Deep cloned object
|
|
20
|
+
* cloneDeep({ a: { b: [1, 2] } })
|
|
21
|
+
* // => { a: { b: [1, 2] } } — fully independent copy
|
|
22
|
+
*
|
|
23
|
+
* cloneDeep(new Date('2024-01-01'))
|
|
24
|
+
* // => Date with the same timestamp
|
|
25
|
+
*
|
|
26
|
+
* // Primitives pass through unchanged
|
|
27
|
+
* cloneDeep(42) // => 42
|
|
28
|
+
* cloneDeep(null) // => null
|
|
28
29
|
* @since 1.9.0
|
|
29
30
|
*/
|
|
30
|
-
declare function
|
|
31
|
+
declare function cloneDeep<T>(obj: T): T;
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* This file is part of helpers4.
|
|
@@ -35,21 +36,19 @@ declare function deepClone<T>(obj: T): T;
|
|
|
35
36
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
36
37
|
*/
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* @
|
|
47
|
-
* @returns The mutated target
|
|
48
|
-
* @since 1.9.0
|
|
39
|
+
* Removes all entries with falsy values (`false`, `null`, `undefined`, `0`, `""`, `NaN`) from an object.
|
|
40
|
+
* Own keys that are prototype-polluting strings (`__proto__`, `constructor`, `prototype`) are
|
|
41
|
+
* silently skipped.
|
|
42
|
+
* @param obj - The source object
|
|
43
|
+
* @returns A new object containing only entries with truthy values
|
|
44
|
+
* @example
|
|
45
|
+
* compact({ a: 1, b: null, c: '', d: 0, e: 'hello' })
|
|
46
|
+
* // => { a: 1, e: 'hello' }
|
|
47
|
+
* @since 2.0.0
|
|
49
48
|
*/
|
|
50
|
-
declare function
|
|
51
|
-
declare function
|
|
52
|
-
declare function
|
|
49
|
+
declare function compact<T extends Record<string, unknown>>(obj: T): Partial<T>;
|
|
50
|
+
declare function compact(obj: undefined): undefined;
|
|
51
|
+
declare function compact(obj: null): null;
|
|
53
52
|
|
|
54
53
|
/**
|
|
55
54
|
* This file is part of helpers4.
|
|
@@ -168,30 +167,109 @@ declare function equalsShallow(objA: unknown, objB: unknown): boolean;
|
|
|
168
167
|
* Copyright (C) 2025 baxyz
|
|
169
168
|
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
170
169
|
*/
|
|
170
|
+
/**
|
|
171
|
+
* Keys that must never be set on a plain object (prototype-pollution guard).
|
|
172
|
+
* @ignore
|
|
173
|
+
*/
|
|
174
|
+
declare const UNSAFE_KEY_LIST: readonly ["__proto__", "constructor", "prototype"];
|
|
175
|
+
/** @ignore */
|
|
176
|
+
type UnsafeKey = (typeof UNSAFE_KEY_LIST)[number];
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* This file is part of helpers4.
|
|
180
|
+
* Copyright (C) 2025 baxyz
|
|
181
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Parses a `[n]` bracket index: numeric index strings become `number`, anything else stays `string`.
|
|
186
|
+
* @ignore
|
|
187
|
+
*/
|
|
188
|
+
type BracketKey<S extends string> = S extends `${number}` ? number : S;
|
|
189
|
+
/**
|
|
190
|
+
* Parses consecutive bracket segments: `'[0][1]'` → `[0, 1]`.
|
|
191
|
+
* @ignore
|
|
192
|
+
*/
|
|
193
|
+
type ParseBrackets<S extends string> = S extends `[${infer Idx}]${infer Rest}` ? [BracketKey<Idx>, ...ParseBrackets<Rest>] : [];
|
|
194
|
+
/**
|
|
195
|
+
* Parses a single dot-segment (no dots inside): `'arr[0]'` → `['arr', 0]`.
|
|
196
|
+
* @ignore
|
|
197
|
+
*/
|
|
198
|
+
type ParseSegment<S extends string> = S extends `${infer Name}[${infer Idx}]${infer Rest}` ? Name extends '' ? [BracketKey<Idx>, ...ParseBrackets<Rest>] : [Name, BracketKey<Idx>, ...ParseBrackets<Rest>] : [S];
|
|
199
|
+
/**
|
|
200
|
+
* Converts a dot/bracket path string literal into a `PropertyKey` tuple.
|
|
201
|
+
*
|
|
202
|
+
* - Dot segments produce string keys: `'a.1'` → `['a', '1']`
|
|
203
|
+
* - Bracket segments produce number keys: `'a[1]'` → `['a', 1]`
|
|
204
|
+
*
|
|
205
|
+
* Limitation: edge-case paths handled specially at runtime (empty string `''`,
|
|
206
|
+
* bare `'.'`, consecutive dots `'a..b'`) are not modelled at the type level.
|
|
207
|
+
*
|
|
208
|
+
* @ignore
|
|
209
|
+
*/
|
|
210
|
+
type ParseStringPath<S extends string> = string extends S ? readonly PropertyKey[] : S extends `${infer Head}.${infer Tail}` ? [...ParseSegment<Head>, ...ParseStringPath<Tail>] : ParseSegment<S>;
|
|
211
|
+
/**
|
|
212
|
+
* Normalises both path forms into a `PropertyKey` tuple for use with `DeepGet`/`DeepSet`.
|
|
213
|
+
* - `string` literal → parsed via `ParseStringPath`
|
|
214
|
+
* - `readonly PropertyKey[]` → used as-is
|
|
215
|
+
* @ignore
|
|
216
|
+
*/
|
|
217
|
+
type ParsePath<P extends string | readonly PropertyKey[]> = P extends string ? ParseStringPath<P> : P extends readonly PropertyKey[] ? P : never;
|
|
218
|
+
/**
|
|
219
|
+
* Returns `true` if any key in the tuple is a prototype-polluting string.
|
|
220
|
+
* Used to make `set` return `T` unchanged (instead of a misleading `DeepSet` type)
|
|
221
|
+
* when the path is rejected by the runtime guard.
|
|
222
|
+
* @ignore
|
|
223
|
+
*/
|
|
224
|
+
type ContainsUnsafeStringKey<Keys extends readonly PropertyKey[]> = Keys extends readonly [infer K, ...infer Rest extends readonly PropertyKey[]] ? K extends UnsafeKey ? true : ContainsUnsafeStringKey<Rest> : false;
|
|
225
|
+
/**
|
|
226
|
+
* Converts a union to an intersection: `A | B | C` → `A & B & C`.
|
|
227
|
+
* Used to derive the return type of `mergeDeep` from a tuple of source types.
|
|
228
|
+
* @ignore
|
|
229
|
+
*/
|
|
230
|
+
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
|
|
231
|
+
/**
|
|
232
|
+
* Return type of `mergeDeep`: intersection of all source types.
|
|
233
|
+
*
|
|
234
|
+
* @ignore
|
|
235
|
+
* @example
|
|
236
|
+
* MergeResult<[{ a: number }, { b: string }]> // { a: number } & { b: string }
|
|
237
|
+
* MergeResult<[{ a: { b: number } }, { a: { c: string } }]> // { a: { b: number } & { c: string } }
|
|
238
|
+
*/
|
|
239
|
+
type MergeResult<T extends object[]> = UnionToIntersection<T[number]>;
|
|
171
240
|
/**
|
|
172
241
|
* Resolves the value type at a given path within T.
|
|
173
242
|
* Returns `unknown` when the path doesn't match the shape of T.
|
|
174
243
|
*
|
|
175
|
-
* @
|
|
244
|
+
* @ignore
|
|
176
245
|
* @example
|
|
177
246
|
* DeepGet<{ a: { b: number } }, ['a', 'b']> // => number
|
|
178
247
|
* DeepGet<{ a: { b: number } }, ['a', 'x']> // => unknown
|
|
179
248
|
* DeepGet<{ a: number }, []> // => { a: number }
|
|
180
249
|
*/
|
|
181
250
|
type DeepGet<T, Path extends readonly PropertyKey[]> = Path extends readonly [] ? T : Path extends readonly [infer K, ...infer Rest extends readonly PropertyKey[]] ? K extends keyof T ? DeepGet<T[K], Rest> : unknown : unknown;
|
|
251
|
+
/**
|
|
252
|
+
* Builds a brand-new nested object type from the remaining path segments and a leaf value type.
|
|
253
|
+
* Used by `DeepSet` once a path segment no longer matches an existing key on T.
|
|
254
|
+
* @ignore
|
|
255
|
+
*/
|
|
256
|
+
type BuildPath<Path extends readonly PropertyKey[], V> = Path extends readonly [infer K extends PropertyKey, ...infer Rest extends readonly PropertyKey[]] ? Record<K, BuildPath<Rest, V>> : V;
|
|
182
257
|
/**
|
|
183
258
|
* Produces the type of T after setting the value at Path to V.
|
|
184
|
-
*
|
|
185
|
-
*
|
|
259
|
+
* When a key in Path is not present at the corresponding level of T, that level (and
|
|
260
|
+
* everything below it) is added as a new field instead of resolving to `never` — matching
|
|
261
|
+
* the runtime behavior of `set`, which creates intermediate objects as needed.
|
|
186
262
|
*
|
|
187
|
-
* @
|
|
263
|
+
* @ignore
|
|
188
264
|
* @example
|
|
189
265
|
* DeepSet<{ a: { b: number } }, ['a', 'b'], string>
|
|
190
266
|
* // => { a: { b: string } }
|
|
267
|
+
* DeepSet<{ a: { b: number } }, ['a', 'c'], string>
|
|
268
|
+
* // => { a: { b: number } & { c: string } }
|
|
191
269
|
*/
|
|
192
|
-
type DeepSet<T, Path extends readonly PropertyKey[], V> = Path extends readonly [] ?
|
|
270
|
+
type DeepSet<T, Path extends readonly PropertyKey[], V> = Path extends readonly [] ? V : Path extends readonly [infer K extends PropertyKey, ...infer Rest extends readonly PropertyKey[]] ? K extends keyof T ? {
|
|
193
271
|
[P in keyof T]: P extends K ? DeepSet<T[K], Rest, V> : T[P];
|
|
194
|
-
} :
|
|
272
|
+
} : T & Record<K, BuildPath<Rest, V>> : never;
|
|
195
273
|
|
|
196
274
|
/**
|
|
197
275
|
* This file is part of helpers4.
|
|
@@ -205,29 +283,36 @@ type DeepSet<T, Path extends readonly PropertyKey[], V> = Path extends readonly
|
|
|
205
283
|
* **Two path forms are supported:**
|
|
206
284
|
*
|
|
207
285
|
* 1. **String path** — dot notation (`'a.b.c'`) and bracket notation (`'layers[1].name'`)
|
|
208
|
-
* are both accepted and mixed freely.
|
|
209
|
-
*
|
|
286
|
+
* are both accepted and mixed freely.
|
|
287
|
+
* - Dot segments are always string keys: `'a.1'` → key `'1'` (string).
|
|
288
|
+
* - Bracket segments are always number keys: `'a[1]'` → key `1` (number).
|
|
289
|
+
* - String literal paths give **full compile-time type inference** on the return type.
|
|
290
|
+
* - Dynamic (non-literal) strings return `unknown`.
|
|
210
291
|
*
|
|
211
292
|
* 2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
212
293
|
* no parsing performed. Enables symbol-keyed traversal and compile-time type inference:
|
|
213
294
|
* `get(obj, ['a', 'b'] as const)` infers the return type from the path.
|
|
214
295
|
*
|
|
296
|
+
* Both forms support **all objects** (plain objects, arrays, class instances).
|
|
297
|
+
* Symbol keys are only reachable via the key-array form.
|
|
298
|
+
*
|
|
215
299
|
* @param obj - The object to read from
|
|
216
|
-
* @param path - Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
300
|
+
* @param path - Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
217
301
|
* @param defaultValue - Returned when the path is absent or resolves to `undefined`
|
|
218
302
|
* @returns The value at the path, or `defaultValue`
|
|
219
303
|
* @example
|
|
220
|
-
* get({ a: { b: { c: 42 } } }, 'a.b.c') // => 42
|
|
221
|
-
* get({
|
|
222
|
-
* get({ a: { b: 0 } }, 'a.b', 99) // => 0 (falsy values
|
|
304
|
+
* get({ a: { b: { c: 42 } } }, 'a.b.c') // => 42 (inferred: number | undefined)
|
|
305
|
+
* get({ layers: [{ name: 'bg' }] }, 'layers[0].name') // => 'bg' (inferred: string | undefined)
|
|
306
|
+
* get({ a: { b: 0 } }, 'a.b', 99) // => 0 (falsy values returned as-is)
|
|
223
307
|
* get({ a: 1 }, 'b.c', 'default') // => 'default'
|
|
224
308
|
*
|
|
225
309
|
* const id = Symbol('id')
|
|
226
|
-
* get({ [id]: 'alice' }, [id]) // => 'alice' (symbol key)
|
|
310
|
+
* get({ [id]: 'alice' }, [id]) // => 'alice' (symbol key, inferred: string | undefined)
|
|
227
311
|
* @since 1.9.0
|
|
228
312
|
*/
|
|
229
|
-
declare function get<
|
|
230
|
-
declare function get
|
|
313
|
+
declare function get<D>(obj: null | undefined, path: string | readonly PropertyKey[], defaultValue: D): D;
|
|
314
|
+
declare function get(obj: null | undefined, path: string | readonly PropertyKey[]): undefined;
|
|
315
|
+
declare function get<T extends object, const P extends string | readonly PropertyKey[]>(obj: T | null | undefined, path: P, defaultValue?: DeepGet<T, ParsePath<P>>): DeepGet<T, ParsePath<P>> | undefined;
|
|
231
316
|
|
|
232
317
|
/**
|
|
233
318
|
* This file is part of helpers4.
|
|
@@ -239,6 +324,9 @@ declare function get<T extends object, const Path extends readonly PropertyKey[]
|
|
|
239
324
|
*
|
|
240
325
|
* A thin, typed wrapper around `Object.groupBy` (ES2024) that works on
|
|
241
326
|
* older targets and provides stricter return-type inference.
|
|
327
|
+
* `null` and `undefined` are treated as empty arrays and return `{}`.
|
|
328
|
+
* Items whose computed key is a prototype-polluting string (`__proto__`,
|
|
329
|
+
* `constructor`, `prototype`) are silently skipped.
|
|
242
330
|
*
|
|
243
331
|
* @param items - The array to group
|
|
244
332
|
* @param keyFn - Function that returns the group key for each item
|
|
@@ -246,9 +334,11 @@ declare function get<T extends object, const Path extends readonly PropertyKey[]
|
|
|
246
334
|
* @example
|
|
247
335
|
* groupBy([1, 2, 3, 4], n => n % 2 === 0 ? 'even' : 'odd')
|
|
248
336
|
* // => { odd: [1, 3], even: [2, 4] }
|
|
337
|
+
* groupBy(null, n => n % 2 === 0 ? 'even' : 'odd')
|
|
338
|
+
* // => {}
|
|
249
339
|
* @since 2.0.0
|
|
250
340
|
*/
|
|
251
|
-
declare function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (item: T) => K): Partial<Record<K, T[]>>;
|
|
341
|
+
declare function groupBy<T, K extends PropertyKey>(items: readonly T[] | null | undefined, keyFn: (item: T) => K): Partial<Record<K, T[]>>;
|
|
252
342
|
|
|
253
343
|
/**
|
|
254
344
|
* This file is part of helpers4.
|
|
@@ -258,6 +348,9 @@ declare function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (
|
|
|
258
348
|
/**
|
|
259
349
|
* Returns a new object with keys and values swapped.
|
|
260
350
|
* If multiple keys share the same value, the last one wins.
|
|
351
|
+
* `null` and `undefined` are treated as empty objects and return `{}`.
|
|
352
|
+
* Entries whose source key **or** value is a prototype-polluting string
|
|
353
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
261
354
|
*
|
|
262
355
|
* @param obj - The object whose keys and values are to be swapped
|
|
263
356
|
* @returns A new object with values as keys and original keys as values
|
|
@@ -267,9 +360,12 @@ declare function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (
|
|
|
267
360
|
*
|
|
268
361
|
* invert({ one: 1, two: 2 })
|
|
269
362
|
* // => { 1: 'one', 2: 'two' }
|
|
363
|
+
*
|
|
364
|
+
* invert(null)
|
|
365
|
+
* // => {}
|
|
270
366
|
* @since 2.0.0
|
|
271
367
|
*/
|
|
272
|
-
declare function invert<K extends string, V extends PropertyKey>(obj: Record<K, V>): Record<V, K>;
|
|
368
|
+
declare function invert<K extends string, V extends PropertyKey>(obj: Record<K, V> | null | undefined): Record<V, K>;
|
|
273
369
|
|
|
274
370
|
/**
|
|
275
371
|
* This file is part of helpers4.
|
|
@@ -278,19 +374,22 @@ declare function invert<K extends string, V extends PropertyKey>(obj: Record<K,
|
|
|
278
374
|
*/
|
|
279
375
|
/**
|
|
280
376
|
* Checks if a plain object has no own enumerable string-keyed properties.
|
|
377
|
+
* `null` and `undefined` are treated as empty objects and return `true`.
|
|
281
378
|
*
|
|
282
379
|
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
283
380
|
* separately if symbol keys matter for your use case.
|
|
284
381
|
*
|
|
285
382
|
* @param value - The object to check
|
|
286
|
-
* @returns `true` if the object has no own enumerable string-keyed properties
|
|
383
|
+
* @returns `true` if the object has no own enumerable string-keyed properties, or if `value` is `null`/`undefined`
|
|
287
384
|
* @example
|
|
288
385
|
* isEmpty({}) // => true
|
|
386
|
+
* isEmpty(null) // => true
|
|
387
|
+
* isEmpty(undefined) // => true
|
|
289
388
|
* isEmpty({ a: 1 }) // => false
|
|
290
389
|
* isEmpty({ a: undefined }) // => false (key exists)
|
|
291
390
|
* @since 2.0.3
|
|
292
391
|
*/
|
|
293
|
-
declare function isEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
392
|
+
declare function isEmpty(value: Record<PropertyKey, unknown> | null | undefined): boolean;
|
|
294
393
|
|
|
295
394
|
/**
|
|
296
395
|
* This file is part of helpers4.
|
|
@@ -299,18 +398,22 @@ declare function isEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
|
299
398
|
*/
|
|
300
399
|
/**
|
|
301
400
|
* Checks if a plain object has at least one own enumerable string-keyed property.
|
|
401
|
+
* `null` and `undefined` are treated as empty objects and return `false`.
|
|
302
402
|
*
|
|
303
403
|
* Symbol-keyed properties are not counted. Use `Object.getOwnPropertySymbols`
|
|
304
404
|
* separately if symbol keys matter for your use case.
|
|
305
405
|
*
|
|
306
406
|
* @param value - The object to check
|
|
307
|
-
* @returns `true` if the object has at least one own enumerable string-keyed property
|
|
407
|
+
* @returns `true` if the object has at least one own enumerable string-keyed property; `false` for empty, `null`, or `undefined`.
|
|
408
|
+
* Acts as a type guard: the `if` branch narrows `Record<PropertyKey, unknown> | null | undefined` to `Record<PropertyKey, unknown>`.
|
|
308
409
|
* @example
|
|
309
410
|
* isNonEmpty({ a: 1 }) // => true
|
|
310
411
|
* isNonEmpty({}) // => false
|
|
412
|
+
* isNonEmpty(null) // => false
|
|
413
|
+
* isNonEmpty(undefined) // => false
|
|
311
414
|
* @since 2.0.3
|
|
312
415
|
*/
|
|
313
|
-
declare function isNonEmpty(value: Record<PropertyKey, unknown>):
|
|
416
|
+
declare function isNonEmpty(value: Record<PropertyKey, unknown> | null | undefined): value is Record<PropertyKey, unknown>;
|
|
314
417
|
|
|
315
418
|
/**
|
|
316
419
|
* This file is part of helpers4.
|
|
@@ -325,7 +428,8 @@ declare function isNonEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
|
325
428
|
* when `mapKey` is omitted the original keys are preserved.
|
|
326
429
|
*
|
|
327
430
|
* Note: if two different keys map to the same output key the last one wins
|
|
328
|
-
* (insertion order).
|
|
431
|
+
* (insertion order). Entries whose mapped key is a prototype-polluting string
|
|
432
|
+
* (`__proto__`, `constructor`, `prototype`) are silently skipped.
|
|
329
433
|
*
|
|
330
434
|
* @param obj - The source object
|
|
331
435
|
* @param mapValue - Callback called with `(value, key)` for each entry.
|
|
@@ -344,7 +448,69 @@ declare function isNonEmpty(value: Record<PropertyKey, unknown>): boolean;
|
|
|
344
448
|
* // => { A: 10, B: 20 }
|
|
345
449
|
* @since 2.0.0
|
|
346
450
|
*/
|
|
347
|
-
declare function map<TObj extends Record<string, unknown>, TVal = TObj[keyof TObj], TKey extends PropertyKey = keyof TObj>(obj: TObj, mapValue?: (value: TObj[keyof TObj], key: keyof TObj) => TVal, mapKey?: (key: keyof TObj, value: TObj[keyof TObj]) => TKey): Record<TKey, TVal>;
|
|
451
|
+
declare function map<TObj extends Record<string, unknown>, TVal = TObj[keyof TObj], TKey extends PropertyKey = keyof TObj>(obj: TObj | null | undefined, mapValue?: (value: TObj[keyof TObj], key: keyof TObj) => TVal, mapKey?: (key: keyof TObj, value: TObj[keyof TObj]) => TKey): Record<TKey, TVal>;
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* This file is part of helpers4.
|
|
455
|
+
* Copyright (C) 2025 baxyz
|
|
456
|
+
* SPDX-License-Identifier: LGPL-3.0-or-later
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Merges two or more objects deeply, returning a **new** object without mutating any input.
|
|
461
|
+
*
|
|
462
|
+
* Sources are applied left to right. For each key:
|
|
463
|
+
* - If both sides are plain objects → merged recursively into a new object.
|
|
464
|
+
* - Otherwise → **later source wins** (the earlier value is overwritten).
|
|
465
|
+
* - `undefined` in a source **never overwrites** an existing value.
|
|
466
|
+
* - `null` in a source **does overwrite** and prevents a later source from
|
|
467
|
+
* deep-merging into that key: `mergeDeep({ a: { x: 1 } }, { a: null }, { a: { y: 2 } })`
|
|
468
|
+
* → `{ a: { y: 2 } }` — `x: 1` is permanently lost.
|
|
469
|
+
* - Arrays, class instances, and all non-plain-object values are **replaced**, not merged.
|
|
470
|
+
*
|
|
471
|
+
* Own enumerable string and symbol keys of each source are processed at the top level;
|
|
472
|
+
* inherited and non-enumerable properties are skipped. Prototype-polluting keys
|
|
473
|
+
* (`__proto__`, `constructor`, `prototype`) are silently ignored.
|
|
474
|
+
* **Note:** symbol keys inside nested plain-object values are not preserved — they are
|
|
475
|
+
* lost when those values are deep-cloned. Only top-level symbol keys survive.
|
|
476
|
+
*
|
|
477
|
+
* **TypeScript return type — intersection semantics**
|
|
478
|
+
*
|
|
479
|
+
* The return type is `A & B & C …` (intersection of all source types). This is accurate
|
|
480
|
+
* when keys are disjoint or share the same type:
|
|
481
|
+
* ```ts
|
|
482
|
+
* mergeDeep({ a: 1 }, { b: 'x' }) // { a: number } & { b: string } ✓
|
|
483
|
+
* mergeDeep({ a: { b: 1 } }, { a: { c: 2 } }) // { a: { b: number } & { c: number } } ✓
|
|
484
|
+
* ```
|
|
485
|
+
* When the same key carries **incompatible types** across sources, the intersection
|
|
486
|
+
* resolves to `never` for that key — TypeScript surfaces the conflict rather than
|
|
487
|
+
* silently picking a type:
|
|
488
|
+
* ```ts
|
|
489
|
+
* mergeDeep({ a: 1 }, { a: 'x' }) // { a: never } ← type conflict detected
|
|
490
|
+
* ```
|
|
491
|
+
* At runtime the later source always wins (`'x'`), but the `never` type signals that
|
|
492
|
+
* the caller should align their source types. If intentional, cast the result.
|
|
493
|
+
*
|
|
494
|
+
* @param sources - Two or more objects to merge (none are mutated)
|
|
495
|
+
* @returns A new object that is the deep merge of all sources
|
|
496
|
+
* @example
|
|
497
|
+
* mergeDeep({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 })
|
|
498
|
+
* // => { a: 1, b: { c: 2, d: 3 }, e: 4 }
|
|
499
|
+
*
|
|
500
|
+
* // Later source wins for non-object values
|
|
501
|
+
* mergeDeep({ a: 1, arr: [1, 2] }, { a: 2, arr: [3] })
|
|
502
|
+
* // => { a: 2, arr: [3] }
|
|
503
|
+
*
|
|
504
|
+
* // undefined never overwrites
|
|
505
|
+
* mergeDeep({ a: 1 }, { a: undefined })
|
|
506
|
+
* // => { a: 1 }
|
|
507
|
+
*
|
|
508
|
+
* // Three sources, left to right
|
|
509
|
+
* mergeDeep({ x: 1 }, { y: 2 }, { x: 99, z: 3 })
|
|
510
|
+
* // => { x: 99, y: 2, z: 3 }
|
|
511
|
+
* @since 1.9.0
|
|
512
|
+
*/
|
|
513
|
+
declare function mergeDeep<T extends [object, ...object[]]>(...sources: [...T]): MergeResult<T>;
|
|
348
514
|
|
|
349
515
|
/**
|
|
350
516
|
* This file is part of helpers4.
|
|
@@ -372,6 +538,8 @@ declare function omit(obj: null, keys: readonly string[]): null;
|
|
|
372
538
|
*/
|
|
373
539
|
/**
|
|
374
540
|
* Creates a new object with only the specified keys.
|
|
541
|
+
* Keys that are prototype-polluting strings (`__proto__`, `constructor`, `prototype`) are
|
|
542
|
+
* silently skipped.
|
|
375
543
|
* @param obj - The source object
|
|
376
544
|
* @param keys - The keys to pick
|
|
377
545
|
* @returns A new object with only the picked keys
|
|
@@ -446,47 +614,44 @@ declare function safeJsonParse<T>(input: string, fallback: T): T;
|
|
|
446
614
|
/**
|
|
447
615
|
* Sets a value in an object at the given path, creating intermediate objects as needed.
|
|
448
616
|
*
|
|
449
|
-
* **
|
|
617
|
+
* **Two path forms are supported:**
|
|
450
618
|
*
|
|
451
|
-
* 1. **
|
|
452
|
-
* `
|
|
619
|
+
* 1. **String path** — dot notation and bracket notation, mixed freely.
|
|
620
|
+
* - Dot segments are always string keys: `'layers.1.name'` → keys `['layers', '1', 'name']`.
|
|
621
|
+
* - Bracket segments are always number keys: `'layers[1].name'` → keys `['layers', 1, 'name']`.
|
|
622
|
+
* - String literal paths give **full compile-time type inference** on the return type.
|
|
623
|
+
* - Dynamic (non-literal) strings return `T` (same object type).
|
|
453
624
|
*
|
|
454
|
-
* 2. **
|
|
455
|
-
*
|
|
456
|
-
* Dot and bracket can be mixed freely: `"a[0].b[2].c"`.
|
|
625
|
+
* 2. **Key array** (`PropertyKey[]`) — explicit array of `string | number | symbol` keys,
|
|
626
|
+
* no parsing performed. Enables symbol-keyed access and precise return-type inference.
|
|
457
627
|
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* `["layers", 1, Symbol('id')]`.
|
|
628
|
+
* Both forms support **all objects** (plain objects, arrays, class instances).
|
|
629
|
+
* Symbol keys are only reachable via the key-array form.
|
|
461
630
|
*
|
|
462
631
|
* Intermediate nodes that are absent, `null`, or not an object are replaced with `{}`.
|
|
463
632
|
* Any path containing a string segment equal to `__proto__`, `constructor`, or `prototype`
|
|
464
633
|
* is rejected and the original object is returned unchanged (prototype-pollution guard).
|
|
465
634
|
*
|
|
466
635
|
* @param obj - The object to mutate
|
|
467
|
-
* @param path - Dot/bracket-notation string or explicit `PropertyKey[]`
|
|
636
|
+
* @param path - Dot/bracket-notation string literal or explicit `PropertyKey[]`
|
|
468
637
|
* @param value - Value to assign at the path
|
|
469
|
-
* @returns The mutated object (same reference)
|
|
638
|
+
* @returns The mutated object (same reference, narrowed type)
|
|
470
639
|
* @example
|
|
471
|
-
* //
|
|
640
|
+
* // Dot notation — "1" stays a string key
|
|
472
641
|
* set({}, 'a.b.c', 42)
|
|
473
642
|
* // => { a: { b: { c: 42 } } }
|
|
474
643
|
*
|
|
475
|
-
*
|
|
476
|
-
* // => { layers: { '1': { name: 'bg' } } }
|
|
477
|
-
*
|
|
478
|
-
* // 2. Bracket notation — [1] becomes a number key
|
|
644
|
+
* // Bracket notation — [1] becomes a number key
|
|
479
645
|
* set({ layers: [{}, { name: 'old' }] }, 'layers[1].name', 'bg')
|
|
480
646
|
* // => { layers: [{}, { name: 'bg' }] }
|
|
481
647
|
*
|
|
482
|
-
* //
|
|
648
|
+
* // Key array — supports symbols
|
|
483
649
|
* const id = Symbol('id')
|
|
484
650
|
* set({}, ['user', id], 'alice')
|
|
485
651
|
* // => { user: { [id]: 'alice' } }
|
|
486
652
|
* @since 1.9.0
|
|
487
653
|
*/
|
|
488
|
-
declare function set(obj:
|
|
489
|
-
declare function set<T extends object, const Path extends readonly PropertyKey[], V extends DeepGet<T, Path>>(obj: T, path: Path, value: V): DeepSet<T, Path, V>;
|
|
654
|
+
declare function set<T extends object, const P extends string | readonly PropertyKey[], V extends DeepGet<T, ParsePath<P>>>(obj: T, path: P, value: V): string extends P ? T : readonly PropertyKey[] extends P ? T : ContainsUnsafeStringKey<ParsePath<P>> extends true ? T : DeepSet<T, ParsePath<P>, V>;
|
|
490
655
|
|
|
491
|
-
export {
|
|
656
|
+
export { cloneDeep, compact, diff, equalsDeep, equalsShallow, get, groupBy, invert, isEmpty, isNonEmpty, map, mergeDeep, omit, pick, removeUndefinedNull, safeJsonParse, set };
|
|
492
657
|
export type { DiffOptions, DiffResult };
|