Object manipulation and transformation utilities providing type-safe operations for mapping, merging, filtering, and analyzing object structures.
Transform object key-value pairs with type-safe operations.
/**
* Map key/value pairs for an object, and construct a new one
* @param obj - Object to transform
* @param fn - Transformation function returning new [key, value] pair or undefined to omit
* @returns New object with transformed key-value pairs
*/
function objectMap<K extends string, V, NK extends string | number | symbol = K, NV = V>(
obj: Record<K, V>,
fn: (key: K, value: V) => [NK, NV] | undefined
): Record<NK, NV>;Usage Examples:
import { objectMap } from "@antfu/utils";
// Transform keys and values
const transformed = objectMap(
{ a: 1, b: 2 },
(k, v) => [k.toUpperCase(), v.toString()]
);
// { A: '1', B: '2' }
// Swap key/value
const swapped = objectMap({ a: 1, b: 2 }, (k, v) => [v, k]);
// { 1: 'a', 2: 'b' }
// Filter keys
const filtered = objectMap(
{ a: 1, b: 2, c: 3 },
(k, v) => k === 'a' ? undefined : [k, v]
);
// { b: 2, c: 3 }Type-safe object key checking and manipulation.
/**
* Type guard for any key, `k`.
* Marks `k` as a key of `T` if `k` is in `obj`.
* @param obj - Object to query for key `k`
* @param k - Key to check existence in `obj`
* @returns True if key exists in object
*/
function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T;
/**
* Strict typed `Object.keys`
* @param obj - Object to get keys from
* @returns Array of object keys with proper typing
*/
function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`>;
/**
* Strict typed `Object.entries`
* @param obj - Object to get entries from
* @returns Array of [key, value] pairs with proper typing
*/
function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]>;Usage Examples:
import { isKeyOf, objectKeys, objectEntries } from "@antfu/utils";
const user = { name: "Alice", age: 30, active: true };
// Safe key checking
function getValue(key: string) {
if (isKeyOf(user, key)) {
return user[key]; // TypeScript knows key is valid
}
return undefined;
}
// Typed object iteration
const keys = objectKeys(user); // ("name" | "age" | "active")[]
const entries = objectEntries(user); // ["name" | "age" | "active", string | number | boolean][]
// Process all entries safely
entries.forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});Advanced object merging with deep property merging capabilities.
/**
* Deep merge objects
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
* @param target - Target object (will be mutated)
* @param sources - Source objects to merge
* @returns The merged target object
*/
function deepMerge<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;
/**
* Deep merge with array merging
* Differs from `deepMerge` in that it merges arrays instead of overriding them.
* The first argument is the target object, the rest are the sources.
* The target object will be mutated and returned.
* @param target - Target object (will be mutated)
* @param sources - Source objects to merge
* @returns The merged target object
*/
function deepMergeWithArray<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S>;Usage Examples:
import { deepMerge, deepMergeWithArray } from "@antfu/utils";
// Basic deep merge
const target = { a: 1, nested: { x: 10 } };
const source = { b: 2, nested: { y: 20 } };
const merged = deepMerge(target, source);
// { a: 1, b: 2, nested: { x: 10, y: 20 } }
// Array merging behavior difference
const withArrays = { items: [1, 2], data: { count: 1 } };
const moreArrays = { items: [3, 4], data: { total: 10 } };
// Standard deep merge (arrays are replaced)
const standardMerge = deepMerge({ ...withArrays }, moreArrays);
// { items: [3, 4], data: { count: 1, total: 10 } }
// Array-merging deep merge (arrays are combined)
const arrayMerge = deepMergeWithArray({ ...withArrays }, moreArrays);
// { items: [1, 2, 3, 4], data: { count: 1, total: 10 } }Select and filter object properties with type safety.
/**
* Create a new subset object by giving keys
* @param obj - Source object
* @param keys - Keys to pick from the object
* @param omitUndefined - Whether to omit undefined values
* @returns New object with only the specified keys
*/
function objectPick<O extends object, T extends keyof O>(
obj: O,
keys: T[],
omitUndefined?: boolean
): Pick<O, T>;
/**
* Clear undefined fields from an object. It mutates the object
* @param obj - Object to clean (mutated)
* @returns The cleaned object
*/
function clearUndefined<T extends object>(obj: T): T;Usage Examples:
import { objectPick, clearUndefined } from "@antfu/utils";
const user = {
id: 1,
name: "Alice",
email: "alice@example.com",
age: 30,
password: "secret",
settings: undefined
};
// Pick specific properties
const publicUser = objectPick(user, ["id", "name", "email"]);
// { id: 1, name: "Alice", email: "alice@example.com" }
// Pick with undefined handling
const withUndefined = objectPick(user, ["name", "settings"]);
// { name: "Alice", settings: undefined }
const withoutUndefined = objectPick(user, ["name", "settings"], true);
// { name: "Alice" }
// Clean undefined values (mutates original)
const messy = { a: 1, b: undefined, c: "hello", d: undefined };
clearUndefined(messy); // messy is now { a: 1, c: "hello" }Safe property existence checking utilities.
/**
* Determines whether an object has a property with the specified name
* Safe alternative to obj.hasOwnProperty(prop)
* @param obj - Object to check
* @param v - Property key to check
* @returns True if object has the specified property
*/
function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean;Usage Examples:
import { hasOwnProperty } from "@antfu/utils";
const obj = { a: 1, b: 2 };
const nullObj = null;
// Safe property checking
if (hasOwnProperty(obj, 'a')) {
console.log(obj.a); // Safe to access
}
// Works safely with null/undefined
if (hasOwnProperty(nullObj, 'a')) { // false, no error
console.log("Won't execute");
}
// Check for inherited vs own properties
const child = Object.create({ inherited: true });
child.own = 'value';
console.log(hasOwnProperty(child, 'own')); // true
console.log(hasOwnProperty(child, 'inherited')); // falseGenerate unique identifiers for objects.
/**
* Get an object's unique identifier
* Same object will always return the same id
* Expect argument to be a non-primitive object/array. Primitive values will be returned as is.
* @param obj - Object to get identifier for
* @returns Unique string identifier for the object
*/
function objectId(obj: WeakKey): string;Usage Examples:
import { objectId } from "@antfu/utils";
const obj1 = { name: "Alice" };
const obj2 = { name: "Alice" }; // Different object, same content
const obj3 = obj1; // Same object reference
// Generate unique IDs
const id1 = objectId(obj1); // e.g., "abc123def"
const id2 = objectId(obj2); // e.g., "xyz789ghi" (different from id1)
const id3 = objectId(obj3); // Same as id1 (same object)
// Primitive values are returned as-is
const primitiveId = objectId("hello" as any); // "hello"
// Useful for object tracking and caching
const cache = new Map();
function expensiveOperation(obj: object) {
const id = objectId(obj);
if (cache.has(id)) {
return cache.get(id);
}
const result = /* expensive computation */;
cache.set(id, result);
return result;
}