import type {OmitStrict} from './types.js'; /** * Returns a boolean indicating if `value` is * a plain object, possibly created with `Object.create(null)`. * But warning! This fails for some obscure corner cases, use a proper library for weird things. */ export const is_plain_object = (value: any): boolean => value ? value.constructor === Object || value.constructor === undefined : false; /** * Iterated keys in `for..in` are always returned as strings, * so to prevent usage errors the key type of `mapper` is always a string. * Symbols are not enumerable as keys, so they're excluded. */ export const map_record = ( obj: Record, mapper: (value: T, key: string) => U, ): Record => { const result = {} as Record; for (const key in obj) { result[key] = mapper(obj[key], key); } return result; }; /** * Creates a new object without the specified `keys`. */ export const omit = , K extends keyof T>( obj: T, keys: Array, ): OmitStrict => { const result = {} as T; for (const key in obj) { if (!keys.includes(key as any)) { result[key] = obj[key]; } } return result; }; /** * Creates a new object with properties that pass the `should_pick` predicate. */ export const pick_by = , K extends string | number>( obj: T, should_pick: (value: any, key: K) => boolean, ): Partial => { const result = {} as Partial; for (const key in obj) { const value = obj[key]; if (should_pick(value, key as any)) { result[key] = value; } } return result; }; /** * A commonly used form of `pick_by`. * * @returns `obj` with all `undefined` properties removed * * @see {@link https://github.com/Microsoft/TypeScript/issues/13195} */ export const omit_undefined = >(obj: T): T => pick_by(obj, (v) => v !== undefined) as T; /** * A more explicit form of `{put_this_first: obj.put_this_first, ...obj}`. */ export const reorder = , K extends string | number>( obj: T, keys: Array, ): T => { const result = {} as T; for (const k of keys) result[k] = obj[k]; // overwriting is probably faster than using // a `Set` to track what's already been added for (const k in obj) result[k] = obj[k]; return result; }; /** * Frozen empty object with no properties, good for options default values. */ export const EMPTY_OBJECT: Record & object = Object.freeze({}); /** * Performs a depth-first traversal of an object's enumerable properties, * calling `cb` for every key and value with the current `obj` context. * @param cb - receives the key, value, and `obj` for every enumerable property on `obj` and its descendents */ export const traverse = (obj: any, cb: (key: string, value: any, obj: any) => void): void => { if (!obj || typeof obj !== 'object') return; for (const k in obj) { const v = obj[k]; cb(k, v, obj); traverse(v, cb); } }; export const transform_empty_object_to_undefined = (obj: T): T | undefined => { if (obj && Object.keys(obj).length === 0) { return; } return obj; };