Object
Utilities for transforming and copying plain objects. Import from the root or from koyojs/Object.
// Root import
import { pick, omit, extract, exclude, cloneDeep, cloneShallow } from "koyojs";
// Per-module import
import { extract } from "koyojs/Object";Returns a new object containing only the specified top-level keys from the source object. The original is not mutated. TypeScript infers the return type as `Pick<T, K>` so callers retain full type safety. For nested dot-notation paths, use `extract`.
| Parameter | Type | Description |
|---|---|---|
obj | T | The source object. |
keys | K[] | An array of top-level keys to keep. |
| returns | Pick<T, K> | — |
import { pick } from "koyojs";
const user = { id: 1, name: "Alice", email: "alice@example.com", role: "admin" };
pick(user, ["id", "name"]);
// { id: 1, name: "Alice" }
pick(user, ["email"]);
// { email: "alice@example.com" }
// Strip sensitive fields before sending to the client
const fullUser = {
id: 42,
name: "Alice",
email: "alice@example.com",
passwordHash: "...",
sessionToken: "...",
};
const publicProfile = pick(fullUser, ["id", "name", "email"]);
// { id: 42, name: "Alice", email: "alice@example.com" }
// Build a PATCH payload from only the changed fields
const changes = pick(formState, ["name", "bio"]);
await api.patch(`/users/${userId}`, changes);Returns a new object with the specified top-level keys removed. The inverse of `pick`. The original is not mutated. Return type is `Omit<T, K>`. For nested dot-notation paths, use `exclude`.
| Parameter | Type | Description |
|---|---|---|
obj | T | The source object. |
keys | K[] | An array of top-level keys to exclude. |
| returns | Omit<T, K> | — |
import { omit } from "koyojs";
const user = { id: 1, name: "Alice", password: "s3cr3t", token: "abc" };
omit(user, ["password", "token"]);
// { id: 1, name: "Alice" }
omit(user, ["id"]);
// { name: "Alice", password: "s3cr3t", token: "abc" }
// Sanitize an object before logging — strip secrets
function logRequest(req: Request & { body: Record<string, unknown> }) {
logger.info(omit(req.body, ["password", "creditCard", "ssn"]));
}
// Store a user record without internal tracking fields
const toStore = omit(incomingUser, ["__v", "_id", "createdAt"]);
await db.users.insert(toStore);Like `pick`, but resolves dot-notation nested paths instead of top-level keys. Each path string is split on `.` and the matching leaf value is lifted into a reconstructed nested result object. Missing paths are silently skipped.
| Parameter | Type | Description |
|---|---|---|
obj | T | The source object. |
paths | string[] | Dot-notation paths to extract, e.g. `['a.b.c', 'd']`. |
| returns | Record<string, unknown> | — |
import { extract } from "koyojs";
const config = {
db: { host: "localhost", port: 5432, password: "secret" },
app: { port: 3000, debug: true },
};
extract(config, ["db.host", "db.port", "app.port"]);
// { db: { host: "localhost", port: 5432 }, app: { port: 3000 } }
// Missing paths are skipped silently — no error thrown
extract(config, ["db.host", "db.missing"]);
// { db: { host: "localhost" } }
// Pull specific fields from a deeply nested API response
const response = {
user: {
profile: { name: "Alice", bio: "Engineer" },
auth: { token: "xyz", expiresAt: "2026-01-01" },
},
meta: { requestId: "abc123", latencyMs: 42 },
};
extract(response, ["user.profile.name", "meta.requestId"]);
// { user: { profile: { name: "Alice" } }, meta: { requestId: "abc123" } }
// Build a safe subset of a config to pass to a child service
const safe = extract(fullConfig, ["db.host", "db.port", "app.port"]);Like `omit`, but resolves dot-notation nested paths instead of top-level keys. Removes only the specified leaf, leaving sibling keys and parent objects intact. Operates recursively, so multiple levels can be stripped in one call.
| Parameter | Type | Description |
|---|---|---|
obj | T | The source object. |
paths | string[] | Dot-notation paths to remove, e.g. `['db.password']`. |
| returns | Record<string, unknown> | — |
import { exclude } from "koyojs";
const config = {
db: { host: "localhost", port: 5432, password: "secret" },
app: { port: 3000, debug: true },
};
exclude(config, ["db.password"]);
// { db: { host: "localhost", port: 5432 }, app: { port: 3000, debug: true } }
// Multiple paths — strip password and debug flag
exclude(config, ["db.password", "app.debug"]);
// { db: { host: "localhost", port: 5432 }, app: { port: 3000 } }
// Redact PII from a nested user record before logging
const userRecord = {
id: 99,
profile: { name: "Alice", ssn: "123-45-6789", email: "a@b.com" },
payment: { last4: "4242", cvv: "123", expiry: "12/26" },
};
const safe = exclude(userRecord, ["profile.ssn", "payment.cvv"]);
// {
// id: 99,
// profile: { name: "Alice", email: "a@b.com" },
// payment: { last4: "4242", expiry: "12/26" },
// }
logger.info(safe); // cvv and ssn never leave the serverReturns a fully independent deep copy via a recursive walk. Preserves prototypes (instanceof stays intact), handles circular references, clones Map and Set entries, copies Date and RegExp, and preserves getter/setter descriptors instead of flattening them to values. Safe for class instances and circular graphs where `cloneShallow` would throw.
| Parameter | Type | Description |
|---|---|---|
value | T | The value to deep-clone. |
| returns | T | — |
import { cloneDeep } from "koyojs";
const original = { a: 1, b: { c: [1, 2, 3] } };
const copy = cloneDeep(original);
copy.b.c.push(4);
console.log(original.b.c); // [1, 2, 3] — untouched
// Preserves prototypes
class Point { constructor(public x: number, public y: number) {} }
const p = new Point(1, 2);
const q = cloneDeep(p);
console.log(q instanceof Point); // true
// Handles circular references
const circ: any = { x: 1 };
circ.self = circ;
const safe = cloneDeep(circ);
console.log(safe.self === safe); // true
// Clones Map and Set
const m = new Map([["a", { v: 1 }]]);
const m2 = cloneDeep(m);
m2.get("a")!.v = 99;
console.log(m.get("a")!.v); // 1 — untouchedReturns a deep copy using the platform's built-in `structuredClone`. Fast and correct for plain data structures (objects, arrays, dates, maps, sets). Throws a DataCloneError for non-serializable values such as functions, symbols, and class instances with methods. Use `cloneDeep` when you need prototype preservation or circular-reference safety.
| Parameter | Type | Description |
|---|---|---|
value | T | The value to clone. |
| returns | T | — |
import { cloneShallow } from "koyojs";
const original = { a: 1, b: { c: [1, 2, 3] } };
const copy = cloneShallow(original);
copy.b.c.push(4);
console.log(original.b.c); // [1, 2, 3] — untouched
// Works with dates, maps, sets
const withDate = { ts: new Date("2026-01-01") };
const cloned = cloneShallow(withDate);
cloned.ts.setFullYear(2030);
console.log(withDate.ts.getFullYear()); // 2026 — untouched
// Throws for non-serializable values
cloneShallow(() => {}); // DataCloneError
cloneShallow(new MyClass()); // DataCloneError (if MyClass has methods)