| name | mongez-reinforcements-objects |
| description | Deep path-aware object utilities from @mongez/reinforcements — get/set/has/unset, pick/omit, compact, merge, clone, flatten, walk, diff, and more.
|
Objects
Path-aware reads/writes, deep transforms, structural diff. Import from @mongez/reinforcements.
Path access — get / set / has / unset
get
get<T, P extends Path<T>>(obj: T, path: P, default?): PathValue<T, P>
get<T = any>(obj: any, path: string, default?: T): T
Read by typed dot-notation. Falsy values pass through correctly (no spurious default-substitution on 0 / "" / false).
get({ user: { email: "ada@x.com" } }, "user.email");
get({ user: {} }, "user.email", "n/a");
get(arr, "0.name");
set
set<T>(obj: T, path: string, value: unknown): T
Mutating write by dot-notation. Auto-creates arrays when the next segment is a numeric index.
set({}, "users.0.name", "Ada");
set(obj, "a.b.c", 1);
has
has(obj: any, path: string): boolean
true if the path exists, even when the value is undefined. Use this to distinguish "missing" from "present-but-undefined".
has({ a: { b: 1 } }, "a.b");
has({ a: { b: undefined } }, "a.b");
has({ a: {} }, "a.b");
unset
unset<T>(obj: T, paths: readonly string[]): T
Mutating remove by dot-notation. Returns the same reference.
unset({ a: 1, b: 2 }, ["a"]);
unset({ a: { b: 1, c: 2 } }, ["a.b"]);
Key selection — pick / omit
pick
pick<T, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K>
pick(obj, keys: string[]): Record<string, any>
pick(obj, predicate: (value, key) => boolean): Record<string, any>
pick({ a: 1, b: 2, c: 3 }, ["a", "c"]);
pick({ a: { b: 1, c: 2 } }, ["a.b"]);
pick({ a: 1, b: 2, c: 3 }, v => v > 1);
omit
omit<T, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K>
omit(obj, keys: string[]): Record<string, any>
omit(obj, predicate: (value, key) => boolean): Record<string, any>
Non-mutating; returns a shallow clone minus the keys/paths.
omit({ a: 1, b: 2 }, ["b"]);
omit({ a: { b: 1, c: 2 } }, ["a.b"]);
omit({ a: 1, b: 2 }, (_, k) => k === "a");
only and except are kept as @deprecated aliases of pick and omit — prefer the new names.
Cleanup — compact
compact
compact<T extends Record<string, any>>(obj: T, options?: CompactOptions): Partial<T>
compact<T>(array: T[], options?: CompactOptions): T[]
type CompactOptions = {
predicate?: (value: any) => boolean;
empties?: boolean;
deep?: boolean;
};
Strip "empty" entries from objects/arrays. Default predicate drops null, undefined, and "" only — keeps 0, false, NaN because those are usually meaningful. With empties and deep on by default, parent containers that become empty after recursion are themselves dropped.
compact({ name: "Ada", email: "", phone: null, age: 0 });
compact({ user: { name: "Ada", email: "" }, meta: {} });
compact(["a", "", null, "b"]);
compact({ a: 0, b: -1 }, { predicate: v => v === -1 });
compact({ tags: [], name: "Ada" }, { empties: false });
Typical uses: cleaning API request payloads, building query strings from filter objects, sanitizing form data.
Conditional construction — when
when
when<T extends object>(condition: unknown, value: T | (() => T)): Partial<T>
Return value when condition is truthy, otherwise {}. Built for inline conditional spreading — a key is only present when the condition holds. The value may be a factory function, invoked lazily and only when the condition is truthy, so the cost of building it is skipped otherwise.
const payload = {
name: "Ada",
...when(isAdmin, { role: "admin" }),
};
when(ready, () => ({ config: buildHeavyConfig() }));
Cleaner than ...(condition && { … }) (which can spread false) and reads as intent at the call site.
Deep transforms — merge / clone / flatten / freeze
merge
merge<A, B>(a: A, b: B): A & B
merge(...sources, options?: { arrays?: "replace" | "concat" | "union" }): any
Recursive merge of plain objects. Arrays default to replace; pass an options object as the last argument to change strategy.
merge({ a: { b: 1 } }, { a: { c: 2 } });
merge({ list: [1, 2] }, { list: [3, 4] }, { arrays: "concat" });
merge({ list: [1, 2] }, { list: [2, 3] }, { arrays: "union" });
Class instances are taken from the latest source rather than merged (cloning custom constructors is out of scope).
clone
clone<T>(value: T): T
Deep clone. Handles Date, RegExp, Error (with own props), Map, Set, typed arrays, ArrayBuffer, and circular references via internal WeakMap. Non-plain class instances are returned by reference.
const copy = clone({ user: { name: "Ada" }, tags: ["x"] });
copy.user.name = "Bob";
flatten
flatten(obj: any, options?: {
separator?: string;
keepNested?: boolean;
maxDepth?: number;
}): Record<string, any>
Flatten to a single-level map of dot-keyed paths. Descends into plain objects, arrays, and class instances. Treats Date / RegExp / Map / Set / typed arrays as leaves.
flatten({ a: { b: 1, c: [2, 3] } });
flatten({ a: { b: 1 } }, { separator: "/" });
freeze
freeze<T>(value: T): Readonly<T>
Recursive Object.freeze across plain objects and arrays.
const config = freeze({ api: { url: "..." } });
config.api.url = "x";
Defaults & inversion
defaults
defaults<T>(target: T, ...sources): T
Mutating: fills only undefined keys on target from each source, left to right.
defaults({ a: 1 }, { a: 2, b: 3 });
defaults({}, { a: 1 }, { a: 2 });
invert
invert<K, V>(obj: Record<K, V>): Record<string, K>
Swap keys and values; values are coerced to strings; duplicate values collide last-wins.
invert({ a: 1, b: 2 });
Per-entry mapping
mapValues / mapKeys
mapValues<T, U>(obj: T, fn: (value, key, obj) => U): Record<keyof T & string, U>
mapKeys<T>(obj: T, fn: (key, value, obj) => string): Record<string, T[keyof T]>
mapValues({ a: 1, b: 2 }, v => v * 2);
mapKeys({ a: 1, b: 2 }, k => k.toUpperCase());
map
map<T, U>(obj: T, fn: (key, value, obj) => U): U[]
Map an object to an array.
map({ a: 1, b: 2 }, (k, v) => `${k}=${v}`);
Typed enumeration
keys<T>(obj: T): Array<keyof T & string>
values<T>(obj: T): Array<T[keyof T]>
entries<T>(obj: T): Array<[keyof T & string, T[keyof T]]>
fromEntries<K, V>(entries: Iterable<readonly [K, V]>): Record<K, V>
Typed wrappers around the matching Object.* static methods.
Traversal & comparison
walk
walk(obj: any, visitor: (value, path, parent, key) => void, parentPath?: string): void
Recursive leaf traversal. Descends into plain objects and arrays; calls visitor for every non-container value with the full dot-path.
walk({ a: { b: 1, c: [2, 3] } }, (value, path) => log(path, value));
diff
diff(a: object, b: object): {
added: Record<string, any>;
removed: Record<string, any>;
changed: Record<string, { from: any; to: any }>;
}
Shallow structural diff; uses deep value equality (areEqual) for nested comparison.
diff({ a: 1, b: 2 }, { a: 1, b: 3, c: 4 });
Sorting
sort
sort<T>(obj: T, recursive?: boolean): T
Return a new object with keys sorted alphabetically. Arrays of objects are out of scope — use @mongez/collection sortBy.
sort({ b: 1, a: 2, c: 3 });
sort({ b: { y: 1, x: 2 }, a: 1 });