Skip to main content

stacks-objects

Use when working with object manipulation in Stacks - deep merging with type safety, object mapping/transformation, strict key checking, typed entries/keys, property picking, clearing undefined values, or the DeepMerge utility type. Covers @stacksjs/objects.

Datos de origen

Repositorio
stacksjs/stacks
Última actividad en el origen
10 de octubre de 2026 a las 17:01
Idioma detectado de SKILL.md
inglés
Estrellas
633
Forks
20

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
stacks-objects
description
Use when working with object manipulation in Stacks - deep merging with type safety, object mapping/transformation, strict key checking, typed entries/keys, property picking, clearing undefined values, or the DeepMerge utility type. Covers @stacksjs/objects.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks Object Utilities Type-safe object manipulation helpers, with core functions in index.ts and additional transforms re-exported from transform.ts. ## Key Path - Core package: `storage/framework/core/objects/src/index.ts` (single file, all functions) ## Dependencies - `@stacksjs/types` -- `DeepMerge` type - `@stacksjs/utils` -- `notNullish` filter function - `@stacksjs/validation` -- `isObject` type guard ## Functions ### objectMap -- Transform Object Key/Value Pairs ```typescript function objectMap<K extends string, V, NK extends PropertyKey = K, NV = V>( obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined, ): Record<NK, NV> ``` The callback receives `(key, value)` and must return either: - `[newKey, newValue]` tuple to include the entry - `undefined` to filter out the entry Implementation uses `Object.fromEntries(Object.entries(obj).map(...).filter(notNullish))`. ```typescript // Transform values objectMap({ a: 1, b: 2 }, (k, v) => [k, v * 2]) // { a: 2, b: 4 } // Rename keys objectMap({ a: 1, b: 2 }, (k, v) => [`prefix_${k}`, v]) // { prefix_a: 1, prefix_b: 2 } // Swap key/value objectMap({ a: 1, b: 2 }, (k, v) => [v, k]) // { 1: 'a', 2: 'b' } // Filter entries (return undefined to exclude) objectMap({ a: 1, b: 2, c: 3 }, (k, v) => k === 'b' ? undefined : [k, v]) // { a: 1, c: 3 } ``` ### deepMerge -- Deep Merge Objects ```typescript function deepMerge<T extends object = object, S extends object = T>( target: T, ...sources: S[] ): DeepMerge<T, S> ``` Recursively merges source objects into target. Handles multiple sources via recursive spread. Key behaviors: - Only merges "mergeable objects" (objects that are not arrays, checked via `isObject()` and `!Array.isArray()`) - Arrays are NOT deeply merged -- they are replaced entirely - Missing keys in target are created as empty objects `{}` before recursive merge - **Mutates the target object** -- the return value IS the modified target - Processes sources left to right ```typescript const target = { a: 1, nested: { x: 1, y: 2 } } const source = { b: 2, nested: { y: 3, z: 4 } } const result = deepMerge(target, source) // result === target (same reference, mutated) // { a: 1, b: 2, nested: { x: 1, y: 3, z: 4 } } // Multiple sources const merged = deepMerge(defaults, userConfig, overrides) ``` The `DeepMerge<T, S>` type (from `@stacksjs/types`) provides full type inference: ```typescript type DeepMerge<F, S> = MergeInsertions<{ [K in keyof F | keyof S]: K extends keyof S & keyof F ? DeepMerge<F[K], S[K]> : K extends keyof S ? S[K] : K extends keyof F ? F[K] : never }> ``` ### objectPick -- Pick Properties by Key ```typescript function objectPick<O extends object, T extends keyof O>( obj: O, keys: T[], omitUndefined?: boolean, // default: false ): Pick<O, T> ``` Creates a new object with only the specified keys. Uses `reduce` internally. ```typescript const user = { id: 1, name: 'John', email: 'john@test.com', password: 'secret' } objectPick(user, ['id', 'name', 'email']) // { id: 1, name: 'John', email: 'john@test.com' } // With omitUndefined=true, skips keys whose values are undefined const partial = { id: 1, name: 'John', bio: undefined } objectPick(partial, ['id', 'name', 'bio'], true) // { id: 1, name: 'John' } (bio omitted) ``` - Only includes keys that exist in the object (`k in obj` check) - With `omitUndefined=true`, additionally skips keys where `obj[k] === undefined` ### clearUndefined -- Remove Undefined Fields ```typescript function clearUndefined<T extends object>(obj: T): T ``` **Mutates the object** by deleting keys whose values are `undefined`. Returns the same object reference. ```typescript const data = { name: 'John', age: undefined, email: 'john@test.com' } clearUndefined(data) // data is now { name: 'John', email: 'john@test.com' } // Returns same reference as input ``` - Only removes `undefined` values -- `null`, `0`, `''`, `false` are preserved - Uses `delete obj[key]` for removal ### isKeyOf -- Type Guard for Object Keys ```typescript function isKeyOf<T extends object>(obj: T, k: keyof any): k is keyof T ``` Type guard that narrows `k` to `keyof T` if the key exists in `obj`. Uses `k in obj`. ```typescript const config = { debug: true, verbose: false } const key: string = 'debug' if (isKeyOf(config, key)) { // TypeScript knows: key is 'debug' | 'verbose' console.log(config[key]) // type-safe access } ``` ### objectKeys -- Strictly Typed Object.keys ```typescript function objectKeys<T extends object>(obj: T): Array<`${keyof T & (string | number | boolean | null | undefined)}`> ``` Returns `Object.keys(obj)` with a stricter return type than the built-in `Object.keys()` (which returns `string[]`). ```typescript const user = { name: 'John', age: 30 } const keys = objectKeys(user) // Type: ('name' | 'age')[] (not string[]) ``` ### objectEntries -- Strictly Typed Object.entries ```typescript function objectEntries<T extends object>(obj: T): Array<[keyof T, T[keyof T]]> ``` Returns `Object.entries(obj)` with a stricter return type than the built-in `Object.entries()` (which returns `[string, any][]`). ```typescript const user = { name: 'John', age: 30 } const entries = objectEntries(user) // Type: ['name' | 'age', string | number][] ``` ### hasOwnProperty -- Safe Property Check ```typescript function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean ``` Null-safe wrapper around `Object.prototype.hasOwnProperty.call()`. Returns `false` if `obj` is `null` or `undefined`. ```typescript hasOwnProperty({ a: 1 }, 'a') // true hasOwnProperty({ a: 1 }, 'b') // false hasOwnProperty(null, 'a') // false (no throw) hasOwnProperty(undefined, 'a') // false (no throw) ``` ## Additional native transforms The root also exports `omit`, `pickBy`, `omitBy`, `mapValues`, `mapKeys`, `invert`, `isEmptyObject`, `getPath` and `setPath` from `transform.ts`. Use a path array such as `['items', 0, 'name']`, not an assumed dot-string parser. `getPath(source, path, fallback?)` reads a nested value; `setPath(source, path, value)` performs a persistent update by copying containers along the path and sharing unaffected siblings. Treat paths as application-owned inputs; this helper is not a path authorization boundary. Choose `objectPick` for key-based selection and `pickBy` for predicate selection. Read transform signatures for callback argument order; `objectMap`'s `(key, value)` is not a universal convention. Retained evidence: `core/objects/tests/transform.test.ts`. ## Mutation and typing gotchas - `deepMerge` **mutates the target object** -- it does not create a new object. The return value is the same reference as `target`. - `clearUndefined` **mutates the input object** -- it deletes properties in place. - `objectPick` creates a NEW object -- original is not affected. - `objectMap` creates a NEW object via `Object.fromEntries`. - `deepMerge` does NOT deeply merge arrays -- arrays from source replace arrays in target entirely. - `deepMerge` uses `isObject()` from `@stacksjs/validation` combined with `!Array.isArray()` to determine mergeability. - `objectMap` can filter entries by returning `undefined` from the callback -- filtered via `notNullish` from `@stacksjs/utils`. - `objectKeys` return type uses a template literal type to handle non-string keys. - `clearUndefined` only removes `undefined` -- `null` values are preserved. - `hasOwnProperty` accepts `null`/`undefined` objects without throwing. - `objectPick` with `omitUndefined=false` (default) includes keys even if their values are `undefined`, as long as the key exists in the object. - These utilities are also re-exported through `@stacksjs/utils`.
Ver en GitHub