UNPKG

ts-data-forge

Version:

[![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.

175 lines (173 loc) 7.13 kB
/** * A collection of type-safe object utility functions providing functional programming patterns * for object manipulation, including pick, omit, shallow equality checks, and more. * * All functions maintain TypeScript type safety and support both direct and curried usage patterns * for better composition with pipe operations. */ var Obj; (function (Obj) { /** * Performs a shallow equality check on two records using a configurable equality function. * Verifies that both records have the same number of entries and that for every key in the first record, * the corresponding value passes the equality test with the value in the second record. * * @param a - The first record to compare * @param b - The second record to compare * @param eq - Optional equality function (defaults to Object.is for strict equality) * @returns `true` if the records are shallowly equal according to the equality function, `false` otherwise * * @example * ```typescript * // Basic usage with default Object.is equality * Obj.shallowEq({ x: 1, y: 2 }, { x: 1, y: 2 }); // true * Obj.shallowEq({ x: 1 }, { x: 1, y: 2 }); // false (different number of keys) * Obj.shallowEq({ x: 1 }, { x: 2 }); // false (different values) * Obj.shallowEq({}, {}); // true (both empty) * * // String comparisons * Obj.shallowEq({ a: "hello" }, { a: "hello" }); // true * Obj.shallowEq({ a: "hello" }, { a: "world" }); // false * * // Using custom equality function * const caseInsensitiveEq = (a: unknown, b: unknown) => * typeof a === 'string' && typeof b === 'string' * ? a.toLowerCase() === b.toLowerCase() * : a === b; * * Obj.shallowEq({ name: "ALICE" }, { name: "alice" }, caseInsensitiveEq); // true * * // Handling special values * Obj.shallowEq({ x: NaN }, { x: NaN }); // true (Object.is treats NaN === NaN) * Obj.shallowEq({ x: +0 }, { x: -0 }); // false (Object.is distinguishes +0 and -0) * ``` */ Obj.shallowEq = (a, b, eq = Object.is) => { const aEntries = Object.entries(a); const bEntries = Object.entries(b); if (aEntries.length !== bEntries.length) return false; return aEntries.every(([k, v]) => eq(b[k], v)); }; function pick(...args) { switch (args.length) { case 2: { const [record, keys] = args; const keysSet = new Set(keys); return ( // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion Object.fromEntries(Object.entries(record).filter(([k, _v]) => keysSet.has(k)))); } case 1: { const [keys] = args; return (record) => pick(record, keys); } } } Obj.pick = pick; function omit(...args) { switch (args.length) { case 2: { const [record, keys] = args; const keysSet = new Set(keys); return ( // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion Object.fromEntries(Object.entries(record).filter(([k, _v]) => !keysSet.has(k)))); } case 1: { const [keys] = args; return (record) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const result = omit(record, keys); return result; }; } } } Obj.omit = omit; /** * Creates an object from an array of key-value pairs with precise TypeScript typing. * This is a type-safe wrapper around `Object.fromEntries` that provides better type inference * and compile-time guarantees about the resulting object structure. * * **Type Behavior**: * - When entries is a fixed-length tuple, the exact object type is inferred * - When entries has dynamic length with union key types, `Partial` is applied to prevent * incorrect assumptions about which keys will be present * * @template Entries - The readonly array type of key-value entry tuples * @param entries - An array of readonly key-value entry tuples `[key, value]` * @returns An object created from the entries with precise typing * * @example * ```typescript * // Fixed entries with precise typing * const fixedEntries = [ * ['name', 'Alice'], * ['age', 30], * ['active', true] * ] as const; * * const user = Obj.fromEntries(fixedEntries); * // Type: { readonly name: "Alice"; readonly age: 30; readonly active: true } * // Value: { name: "Alice", age: 30, active: true } * * // Simple coordinate example * const coordEntries = [['x', 1], ['y', 3]] as const; * const point = Obj.fromEntries(coordEntries); * // Type: { readonly x: 1; readonly y: 3 } * // Value: { x: 1, y: 3 } * * // Dynamic entries with union keys * const dynamicEntries: Array<['name' | 'email', string]> = [ * ['name', 'Alice'] * // email might or might not be present * ]; * const partialUser = Obj.fromEntries(dynamicEntries); * // Type: Partial<{ name: string; email: string }> * // This prevents assuming both 'name' and 'email' are always present * * // Creating configuration objects * const configEntries = [ * ['apiUrl', 'https://api.example.com'], * ['timeout', 5000], * ['retries', 3], * ['debug', false] * ] as const; * const config = Obj.fromEntries(configEntries); * // Precise types for each configuration value * * // Converting Maps to objects * const settingsMap = new Map([ * ['theme', 'dark'], * ['language', 'en'], * ['notifications', true] * ] as const); * const settings = Obj.fromEntries([...settingsMap]); * * // Building objects from computed entries * const keys = ['a', 'b', 'c'] as const; * const values = [1, 2, 3] as const; * const computedEntries = keys.map((k, i) => [k, values[i]] as const); * const computed = Obj.fromEntries(computedEntries); * // Type reflects the specific key-value associations * * // Error handling with validation * function createUserFromEntries(entries: ReadonlyArray<readonly [string, unknown]>) { * const user = Obj.fromEntries(entries); * // Type is Partial<Record<string, unknown>> - safe for dynamic data * * if ('name' in user && typeof user.name === 'string') { * // Type narrowing works correctly * return { name: user.name, ...user }; * } * throw new Error('Invalid user data'); * } * ``` */ Obj.fromEntries = (entries) => // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion Object.fromEntries(entries); })(Obj || (Obj = {})); export { Obj }; //# sourceMappingURL=object.mjs.map