mapObject
EditTransforms each own enumerable value while preserving its property key.
/** An object whose property values have been replaced with `Value`. */export type MappedObject<T, Value> = { [Key in keyof T]: Value;};
/** Converts TypeScript numeric keys to the strings received at runtime. */export type MapObjectKey<T extends object> = Extract<keyof T, PropertyKey> extends infer Key ? Key extends number ? number extends Key ? string : `${Key}` : Key : never;
const readDescriptorValue = ( descriptor: PropertyDescriptor, receiver: object): unknown => { if (Object.hasOwn(descriptor, "value")) return descriptor.value; return descriptor.get ? Reflect.apply(descriptor.get, receiver, []) : undefined;};
/** Transforms each own enumerable value while preserving its property key. */export const mapObject = <T extends object, Value>( object: T extends readonly unknown[] ? never : T, transform: ( this: void, value: T[keyof T], key: MapObjectKey<T>, object: T ) => Value): Partial<MappedObject<T, Value>> => { if (Array.isArray(object)) { throw new TypeError("object must not be an array"); } const mapped = Object.create(null) as Partial<MappedObject<T, Value>>;
const properties = Reflect.ownKeys(object).flatMap((key) => { const descriptor = Reflect.getOwnPropertyDescriptor(object, key); return descriptor ? [{ key, descriptor }] : []; }); const entries = properties .filter(({ descriptor }) => descriptor.enumerable) .map(({ key, descriptor }) => ({ key, value: readDescriptorValue(descriptor, object) as T[keyof T], }));
for (const { key, value } of entries) { Object.defineProperty(mapped, key, { value: transform(value, key as MapObjectKey<T>, object), enumerable: true, configurable: true, writable: true, }); }
return mapped;};Download
Section titled “Download”wget -O src/lib/mapObject.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/mapObject.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/functions "$temp_dir" && mv "$temp_dir/mapObject.ts" src/lib/mapObject.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import { mapObject } from "./mapObject";
const prices = { apple: 2, pear: 3 };const labels = mapObject(prices, (price, fruit) => `${fruit}: $${price}`);
console.log(labels);// { apple: "apple: $2", pear: "pear: $3" }import { mapObject } from "./mapObject";
const users = { alice: { age: 28 }, bob: { age: 35 },};
console.log(mapObject(users, (user) => user.age));// { alice: 28, bob: 35 }object(T) — Non-array object whose own enumerable values should be transformed.transform((value, key, object) => U) — Produces the replacement value for each property and is invoked without a receiver. Numeric TypeScript keys arrive as their runtime string form.
Returns
Section titled “Returns”Partial<MappedObject<T, U>> — A new null-prototype object containing the
transformed own enumerable keys. Properties are optional in the return type
because TypeScript cannot identify non-enumerable properties statically.
Arrays are rejected with TypeError. Symbol and special keys such as
__proto__ are preserved as safe own data properties.