Skip to content

mapObject

Edit

Transforms each own enumerable value while preserving its property key.

mapObject.ts
/** 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;
};
Terminal
wget -O src/lib/mapObject.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/mapObject.ts
mapObject.example.ts
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" }
mapObject.2.example.ts
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.

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.