ts-data-forge 14.2.0 → 14.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/collections/stack.d.mts +0 -5
- package/dist/collections/stack.d.mts.map +1 -1
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/object/object.d.mts +60 -1
- package/dist/object/object.d.mts.map +1 -1
- package/dist/object/object.mjs +14 -0
- package/dist/object/object.mjs.map +1 -1
- package/package.json +29 -16
- package/src/collections/stack.mts +0 -5
- package/src/collections/stack.test.mts +0 -8
- package/src/object/object.mts +86 -0
- package/src/object/object.test.mts +97 -0
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/ts-data-forge)
|
|
4
4
|
[](https://www.npmjs.com/package/ts-data-forge)
|
|
5
|
-
[](
|
|
6
|
-
[](https://github.com/noshiro-pf/mono/blob/main/libs/ts-data-forge/LICENSE)
|
|
6
|
+
[](https://codecov.io/gh/noshiro-pf/mono)
|
|
7
7
|
|
|
8
8
|
**ts-data-forge** is a TypeScript utility library that provides type-safe functional programming utilities with zero runtime dependencies. It aims to enhance development robustness, maintainability, and correctness by leveraging TypeScript's powerful type system.
|
|
9
9
|
|
|
10
10
|
## Perfect Companion to ts-type-forge
|
|
11
11
|
|
|
12
|
-
**ts-data-forge** is designed as the ideal runtime companion to [**ts-type-forge**](https://github.com/noshiro-pf/ts-type-forge), a powerful type utility library. While ts-type-forge provides compile-time type utilities for advanced TypeScript type manipulation, ts-data-forge complements it with runtime utilities that maintain the same level of type safety.
|
|
12
|
+
**ts-data-forge** is designed as the ideal runtime companion to [**ts-type-forge**](https://github.com/noshiro-pf/mono/tree/main/libs/ts-type-forge), a powerful type utility library. While ts-type-forge provides compile-time type utilities for advanced TypeScript type manipulation, ts-data-forge complements it with runtime utilities that maintain the same level of type safety.
|
|
13
13
|
|
|
14
14
|
Together, they form a complete TypeScript development toolkit:
|
|
15
15
|
|
|
@@ -76,7 +76,7 @@ ts-data-forge works best with strict TypeScript settings:
|
|
|
76
76
|
|
|
77
77
|
## Core Modules
|
|
78
78
|
|
|
79
|
-
### 🎯 [Functional Programming](
|
|
79
|
+
### 🎯 [Functional Programming](https://noshiro-pf.github.io/ts-data-forge/)
|
|
80
80
|
|
|
81
81
|
Essential FP utilities for cleaner, more reliable code.
|
|
82
82
|
|
|
@@ -108,7 +108,7 @@ Type-safe array and tuple utilities with functional programming patterns.
|
|
|
108
108
|
- **Array Utils** - Comprehensive array manipulation functions
|
|
109
109
|
- **Tuple Utils** - Type-safe tuple operations with compile-time guarantees
|
|
110
110
|
|
|
111
|
-
### 📦 [Collections](
|
|
111
|
+
### 📦 [Collections](https://noshiro-pf.github.io/ts-data-forge/)
|
|
112
112
|
|
|
113
113
|
Immutable data structures for safer state management.
|
|
114
114
|
|
|
@@ -677,8 +677,8 @@ export default defineConfig({
|
|
|
677
677
|
|
|
678
678
|
## Contributing
|
|
679
679
|
|
|
680
|
-
Contributions are welcome! Please see [CONTRIBUTING.md](
|
|
680
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](https://github.com/noshiro-pf/mono/blob/main/libs/ts-data-forge/CONTRIBUTING.md) for detailed guidelines on how to contribute to this project.
|
|
681
681
|
|
|
682
682
|
## License
|
|
683
683
|
|
|
684
|
-
This project is licensed under the [Apache License 2.0](
|
|
684
|
+
This project is licensed under the [Apache License 2.0](https://github.com/noshiro-pf/mono/blob/main/libs/ts-data-forge/LICENSE).
|
|
@@ -48,7 +48,6 @@ export type Stack<T> = Readonly<{
|
|
|
48
48
|
*
|
|
49
49
|
* stack.push('first');
|
|
50
50
|
*
|
|
51
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
52
51
|
* stack.push('second');
|
|
53
52
|
*
|
|
54
53
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -83,7 +82,6 @@ export type Stack<T> = Readonly<{
|
|
|
83
82
|
*
|
|
84
83
|
* stack.push('first');
|
|
85
84
|
*
|
|
86
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
87
85
|
* stack.push('second');
|
|
88
86
|
*
|
|
89
87
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -118,7 +116,6 @@ export type Stack<T> = Readonly<{
|
|
|
118
116
|
*
|
|
119
117
|
* stack.push('first');
|
|
120
118
|
*
|
|
121
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
122
119
|
* stack.push('second');
|
|
123
120
|
*
|
|
124
121
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -156,7 +153,6 @@ export type Stack<T> = Readonly<{
|
|
|
156
153
|
*
|
|
157
154
|
* stack.push('first');
|
|
158
155
|
*
|
|
159
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
160
156
|
* stack.push('second');
|
|
161
157
|
*
|
|
162
158
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -215,7 +211,6 @@ export type Stack<T> = Readonly<{
|
|
|
215
211
|
*
|
|
216
212
|
* stack.push('first');
|
|
217
213
|
*
|
|
218
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
219
214
|
* stack.push('second');
|
|
220
215
|
*
|
|
221
216
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.d.mts","sourceRoot":"","sources":["../../src/collections/stack.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC9B
|
|
1
|
+
{"version":3,"file":"stack.d.mts","sourceRoot":"","sources":["../../src/collections/stack.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC;IAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,GAAG,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CAC1B,CAAC,CAAC;AA+IH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,kBAAmB,SAAS,CAAC,EAAE,KAAG,KAAK,CAAC,CAAC,CACpC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.mjs","sources":["../../src/collections/stack.mts"],"sourcesContent":["import { Optional } from '../functional/index.mjs';\nimport { range } from '../iterator/index.mjs';\nimport { asUint32, Uint32 } from '../number/index.mjs';\nimport { type SizeType } from '../types.mjs';\n\n/**\n * Interface for a high-performance stack with LIFO (Last-In, First-Out)\n * behavior.\n *\n * This interface defines a mutable stack data structure where elements are\n * added to and removed from the top, following the Last-In, First-Out\n * principle. The implementation uses a dynamic array for optimal performance,\n * providing O(1) operations for both push and pop operations.\n *\n * **LIFO Behavior:**\n *\n * - **push**: Adds elements to the top of the stack\n * - **pop**: Removes and returns elements from the top of the stack\n * - The last element added is the first element to be removed\n *\n * **Performance Characteristics:**\n *\n * - Push: O(1) amortized (O(n) when buffer needs resizing)\n * - Pop: O(1) always\n * - Size/isEmpty: O(1) always\n * - Memory efficient with automatic garbage collection of removed elements\n *\n * **Use Cases:**\n *\n * - Function call management and recursion\n * - Undo/redo functionality\n * - Expression evaluation and parsing\n * - Depth-first search algorithms\n * - Backtracking algorithms\n * - Browser history management\n *\n * @template T The type of elements stored in the stack.\n */\nexport type Stack<T> = Readonly<{\n /**\n * Checks if the stack is empty.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * // eslint-disable-next-line unicorn/prefer-single-call\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n */\n isEmpty: boolean;\n\n /**\n * The number of elements in the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * // eslint-disable-next-line unicorn/prefer-single-call\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n */\n size: SizeType.Arr;\n\n /**\n * Removes and returns the element at the top of the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * // eslint-disable-next-line unicorn/prefer-single-call\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @returns The element at the top of the stack, or `Optional.none` if the\n * stack is empty.\n */\n pop: () => Optional<T>;\n\n /**\n * Adds an element to the top of the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * // eslint-disable-next-line unicorn/prefer-single-call\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @param value The element to add.\n */\n push: (value: T) => void;\n}>;\n\n/**\n * Class implementation for a stack with LIFO (Last-In, First-Out) behavior\n * using a dynamic array. This implementation provides O(1) amortized push and\n * O(1) pop operations by using a resizable buffer that grows as needed.\n *\n * The underlying array automatically resizes when it becomes full, ensuring\n * that the stack can grow to accommodate any number of elements while\n * maintaining efficient operations.\n *\n * @template T The type of elements in the stack.\n * @implements Stack\n */\nclass StackClass<T> implements Stack<T> {\n /** @internal Initial capacity for new stacks. */\n static readonly #INITIAL_CAPACITY = 8;\n\n /** @internal Dynamic array to store stack elements. */\n #mut_buffer: (T | undefined)[];\n\n /** @internal Current number of elements in the stack. */\n #mut_size: Uint32;\n\n /** @internal Current capacity of the buffer. */\n #mut_capacity: Uint32;\n\n /**\n * Constructs a new StackClass instance.\n *\n * @param initialValues Optional initial values to populate the stack.\n */\n constructor(initialValues: readonly T[] = []) {\n const initialCapacity = asUint32(\n Math.max(StackClass.#INITIAL_CAPACITY, initialValues.length * 2),\n );\n\n this.#mut_buffer = Array.from<unknown, T | undefined>(\n { length: initialCapacity },\n () => undefined,\n );\n\n this.#mut_size = asUint32(0);\n\n this.#mut_capacity = initialCapacity;\n\n // Add initial values\n for (const value of initialValues) {\n this.push(value);\n }\n }\n\n /**\n * @internal\n * Resizes the buffer when it becomes full.\n * Doubles the capacity while preserving all elements.\n */\n #resize(): void {\n const newCapacity = asUint32(this.#mut_capacity * 2);\n\n const newBuffer = Array.from<unknown, T | undefined>(\n { length: newCapacity },\n () => undefined,\n );\n\n // Copy existing elements\n for (const i of range(this.#mut_size)) {\n newBuffer[i] = this.#mut_buffer[i];\n }\n\n this.#mut_buffer = newBuffer;\n\n this.#mut_capacity = newCapacity;\n }\n\n /** @inheritdoc */\n get isEmpty(): boolean {\n return this.#mut_size === 0;\n }\n\n /** @inheritdoc */\n get size(): SizeType.Arr {\n return asUint32(this.#mut_size);\n }\n\n /**\n * Removes and returns the element at the top of the stack (LIFO).\n *\n * This operation removes the element that was added most recently (last-in)\n * and returns it. If the stack is empty, returns `Optional.none`. The\n * operation is guaranteed to be O(1) and does not require any array resizing\n * or memory reallocation.\n *\n * **Time Complexity:** O(1) - constant time operation **Space Complexity:**\n * O(1) - no additional memory allocation\n *\n * @returns An Optional containing the removed element, or `Optional.none` if\n * the stack is empty.\n */\n pop(): Optional<T> {\n if (this.isEmpty) {\n return Optional.none;\n }\n\n this.#mut_size = Uint32.sub(this.#mut_size, 1);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const element = this.#mut_buffer[this.#mut_size]!;\n\n this.#mut_buffer[this.#mut_size] = undefined; // Clear reference for garbage collection\n\n return Optional.some(element);\n }\n\n /**\n * Adds an element to the top of the stack (LIFO).\n *\n * This operation adds the element to the top of the stack, where it will be\n * the first to be popped (last-in, first-out ordering). The operation is\n * amortized O(1), meaning it's O(1) for most operations with occasional O(n)\n * when the buffer needs resizing.\n *\n * **Time Complexity:** O(1) amortized - O(n) only when buffer resize is\n * needed **Space Complexity:** O(1) - constant additional memory per element\n *\n * **Buffer Resizing:** When the internal buffer becomes full, it\n * automatically doubles in size and copies existing elements to maintain the\n * stack structure.\n *\n * @param value The element to add to the top of the stack.\n */\n push(value: T): void {\n // Resize if buffer is full\n if (this.#mut_size === this.#mut_capacity) {\n this.#resize();\n }\n\n this.#mut_buffer[this.#mut_size] = value;\n\n this.#mut_size = Uint32.add(this.#mut_size, 1);\n }\n}\n\n/**\n * Creates a new Stack instance with LIFO (Last-In, First-Out) behavior using a\n * high-performance dynamic array.\n *\n * This factory function creates an optimized stack implementation that\n * maintains excellent performance characteristics for both push and pop\n * operations. The underlying dynamic array automatically resizes to accommodate\n * growing workloads while providing predictable O(1) operations.\n *\n * **Implementation Features:**\n *\n * - **O(1) push operations** (amortized - occasionally O(n) when resizing)\n * - **O(1) pop operations** (always)\n * - **Automatic buffer resizing** - starts at 8 elements, doubles when full\n * - **Memory efficient** - garbage collects removed elements immediately\n * - **Dynamic array design** - eliminates need for complex memory management\n *\n * **Performance Benefits:**\n *\n * - No array shifting required for stack operations\n * - Minimal memory allocation overhead\n * - Predictable performance under high load\n * - Efficient memory usage with automatic cleanup\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * // eslint-disable-next-line unicorn/prefer-single-call\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @template T The type of elements stored in the stack.\n * @param initialValues Optional array of initial elements to populate the\n * stack. Elements will be popped in reverse order of how they appear in the\n * array (last array element will be popped first). If provided, the initial\n * buffer capacity will be at least twice the array length.\n * @returns A new Stack instance optimized for high-performance LIFO operations.\n */\nexport const createStack = <T,>(initialValues?: readonly T[]): Stack<T> =>\n new StackClass<T>(initialValues);\n"],"names":["Optional.none","Optional.some"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stack.mjs","sources":["../../src/collections/stack.mts"],"sourcesContent":["import { Optional } from '../functional/index.mjs';\nimport { range } from '../iterator/index.mjs';\nimport { asUint32, Uint32 } from '../number/index.mjs';\nimport { type SizeType } from '../types.mjs';\n\n/**\n * Interface for a high-performance stack with LIFO (Last-In, First-Out)\n * behavior.\n *\n * This interface defines a mutable stack data structure where elements are\n * added to and removed from the top, following the Last-In, First-Out\n * principle. The implementation uses a dynamic array for optimal performance,\n * providing O(1) operations for both push and pop operations.\n *\n * **LIFO Behavior:**\n *\n * - **push**: Adds elements to the top of the stack\n * - **pop**: Removes and returns elements from the top of the stack\n * - The last element added is the first element to be removed\n *\n * **Performance Characteristics:**\n *\n * - Push: O(1) amortized (O(n) when buffer needs resizing)\n * - Pop: O(1) always\n * - Size/isEmpty: O(1) always\n * - Memory efficient with automatic garbage collection of removed elements\n *\n * **Use Cases:**\n *\n * - Function call management and recursion\n * - Undo/redo functionality\n * - Expression evaluation and parsing\n * - Depth-first search algorithms\n * - Backtracking algorithms\n * - Browser history management\n *\n * @template T The type of elements stored in the stack.\n */\nexport type Stack<T> = Readonly<{\n /**\n * Checks if the stack is empty.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n */\n isEmpty: boolean;\n\n /**\n * The number of elements in the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n */\n size: SizeType.Arr;\n\n /**\n * Removes and returns the element at the top of the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @returns The element at the top of the stack, or `Optional.none` if the\n * stack is empty.\n */\n pop: () => Optional<T>;\n\n /**\n * Adds an element to the top of the stack.\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @param value The element to add.\n */\n push: (value: T) => void;\n}>;\n\n/**\n * Class implementation for a stack with LIFO (Last-In, First-Out) behavior\n * using a dynamic array. This implementation provides O(1) amortized push and\n * O(1) pop operations by using a resizable buffer that grows as needed.\n *\n * The underlying array automatically resizes when it becomes full, ensuring\n * that the stack can grow to accommodate any number of elements while\n * maintaining efficient operations.\n *\n * @template T The type of elements in the stack.\n * @implements Stack\n */\nclass StackClass<T> implements Stack<T> {\n /** @internal Initial capacity for new stacks. */\n static readonly #INITIAL_CAPACITY = 8;\n\n /** @internal Dynamic array to store stack elements. */\n #mut_buffer: (T | undefined)[];\n\n /** @internal Current number of elements in the stack. */\n #mut_size: Uint32;\n\n /** @internal Current capacity of the buffer. */\n #mut_capacity: Uint32;\n\n /**\n * Constructs a new StackClass instance.\n *\n * @param initialValues Optional initial values to populate the stack.\n */\n constructor(initialValues: readonly T[] = []) {\n const initialCapacity = asUint32(\n Math.max(StackClass.#INITIAL_CAPACITY, initialValues.length * 2),\n );\n\n this.#mut_buffer = Array.from<unknown, T | undefined>(\n { length: initialCapacity },\n () => undefined,\n );\n\n this.#mut_size = asUint32(0);\n\n this.#mut_capacity = initialCapacity;\n\n // Add initial values\n for (const value of initialValues) {\n this.push(value);\n }\n }\n\n /**\n * @internal\n * Resizes the buffer when it becomes full.\n * Doubles the capacity while preserving all elements.\n */\n #resize(): void {\n const newCapacity = asUint32(this.#mut_capacity * 2);\n\n const newBuffer = Array.from<unknown, T | undefined>(\n { length: newCapacity },\n () => undefined,\n );\n\n // Copy existing elements\n for (const i of range(this.#mut_size)) {\n newBuffer[i] = this.#mut_buffer[i];\n }\n\n this.#mut_buffer = newBuffer;\n\n this.#mut_capacity = newCapacity;\n }\n\n /** @inheritdoc */\n get isEmpty(): boolean {\n return this.#mut_size === 0;\n }\n\n /** @inheritdoc */\n get size(): SizeType.Arr {\n return asUint32(this.#mut_size);\n }\n\n /**\n * Removes and returns the element at the top of the stack (LIFO).\n *\n * This operation removes the element that was added most recently (last-in)\n * and returns it. If the stack is empty, returns `Optional.none`. The\n * operation is guaranteed to be O(1) and does not require any array resizing\n * or memory reallocation.\n *\n * **Time Complexity:** O(1) - constant time operation **Space Complexity:**\n * O(1) - no additional memory allocation\n *\n * @returns An Optional containing the removed element, or `Optional.none` if\n * the stack is empty.\n */\n pop(): Optional<T> {\n if (this.isEmpty) {\n return Optional.none;\n }\n\n this.#mut_size = Uint32.sub(this.#mut_size, 1);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const element = this.#mut_buffer[this.#mut_size]!;\n\n this.#mut_buffer[this.#mut_size] = undefined; // Clear reference for garbage collection\n\n return Optional.some(element);\n }\n\n /**\n * Adds an element to the top of the stack (LIFO).\n *\n * This operation adds the element to the top of the stack, where it will be\n * the first to be popped (last-in, first-out ordering). The operation is\n * amortized O(1), meaning it's O(1) for most operations with occasional O(n)\n * when the buffer needs resizing.\n *\n * **Time Complexity:** O(1) amortized - O(n) only when buffer resize is\n * needed **Space Complexity:** O(1) - constant additional memory per element\n *\n * **Buffer Resizing:** When the internal buffer becomes full, it\n * automatically doubles in size and copies existing elements to maintain the\n * stack structure.\n *\n * @param value The element to add to the top of the stack.\n */\n push(value: T): void {\n // Resize if buffer is full\n if (this.#mut_size === this.#mut_capacity) {\n this.#resize();\n }\n\n this.#mut_buffer[this.#mut_size] = value;\n\n this.#mut_size = Uint32.add(this.#mut_size, 1);\n }\n}\n\n/**\n * Creates a new Stack instance with LIFO (Last-In, First-Out) behavior using a\n * high-performance dynamic array.\n *\n * This factory function creates an optimized stack implementation that\n * maintains excellent performance characteristics for both push and pop\n * operations. The underlying dynamic array automatically resizes to accommodate\n * growing workloads while providing predictable O(1) operations.\n *\n * **Implementation Features:**\n *\n * - **O(1) push operations** (amortized - occasionally O(n) when resizing)\n * - **O(1) pop operations** (always)\n * - **Automatic buffer resizing** - starts at 8 elements, doubles when full\n * - **Memory efficient** - garbage collects removed elements immediately\n * - **Dynamic array design** - eliminates need for complex memory management\n *\n * **Performance Benefits:**\n *\n * - No array shifting required for stack operations\n * - Minimal memory allocation overhead\n * - Predictable performance under high load\n * - Efficient memory usage with automatic cleanup\n *\n * @example\n *\n * ```ts\n * const stack = createStack<string>();\n *\n * assert.isTrue(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 0);\n *\n * stack.push('first');\n *\n * stack.push('second');\n *\n * assert.isFalse(stack.isEmpty);\n *\n * assert.isTrue(stack.size === 2);\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('second'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.some('first'));\n *\n * assert.deepStrictEqual(stack.pop(), Optional.none);\n *\n * const seededStack = createStack([10, 20, 30]);\n *\n * assert.isTrue(seededStack.size === 3);\n *\n * assert.deepStrictEqual(seededStack.pop(), Optional.some(30));\n * ```\n *\n * @template T The type of elements stored in the stack.\n * @param initialValues Optional array of initial elements to populate the\n * stack. Elements will be popped in reverse order of how they appear in the\n * array (last array element will be popped first). If provided, the initial\n * buffer capacity will be at least twice the array length.\n * @returns A new Stack instance optimized for high-performance LIFO operations.\n */\nexport const createStack = <T,>(initialValues?: readonly T[]): Stack<T> =>\n new StackClass<T>(initialValues);\n"],"names":["Optional.none","Optional.some"],"mappings":"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"}
|
package/dist/object/object.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ArrayElement, type Decrement, type DeepOmit, type DeepPick, type IsFixedLengthList, type IsUnion, type List, type ReadonlyRecord, type RelaxedExclude, type RelaxedOmit, type RelaxedPick, type StrictOmit, type StrictPick, type TypeEq, type UnknownRecord } from 'ts-type-forge';
|
|
1
|
+
import { type ArrayElement, type Decrement, type DeepOmit, type DeepPick, type IsFixedLengthList, type IsUnion, type List, type ReadonlyRecord, type RelaxedExclude, type RelaxedOmit, type RelaxedPick, type StrictOmit, type StrictPick, type ToString, type TypeEq, type UnknownRecord } from 'ts-type-forge';
|
|
2
2
|
/**
|
|
3
3
|
* A collection of type-safe object utility functions providing functional
|
|
4
4
|
* programming patterns for object manipulation, including pick, omit, shallow
|
|
@@ -189,6 +189,52 @@ export declare namespace Obj {
|
|
|
189
189
|
* @returns An object created from the entries with precise typing
|
|
190
190
|
*/
|
|
191
191
|
const fromEntries: <const Entries extends readonly (readonly [PropertyKey, unknown])[]>(entries: Entries) => IsFixedLengthList<Entries> extends true ? TsDataForgeInternals.EntriesToObject<Entries> : TsDataForgeInternals.PartialIfKeyIsUnion<TsDataForgeInternals.KeysOfEntries<Entries>, ReadonlyRecord<TsDataForgeInternals.KeysOfEntries<Entries>, TsDataForgeInternals.ValuesOfEntries<Entries>>>;
|
|
192
|
+
/**
|
|
193
|
+
* Transforms every value of a record, leaving the key set untouched.
|
|
194
|
+
*
|
|
195
|
+
* **Type Behavior**: the result is a mapped type over `keyof R`, so the keys
|
|
196
|
+
* — and their optional modifiers — survive verbatim:
|
|
197
|
+
* `{ a: number; b?: number }` maps to `{ a: V2; b?: V2 }`, and a record with
|
|
198
|
+
* an index signature stays total rather than becoming `Partial`.
|
|
199
|
+
*
|
|
200
|
+
* This is what `Object.fromEntries(Object.entries(record).map(...))` cannot
|
|
201
|
+
* express: an entries array carries no evidence that it still covers every
|
|
202
|
+
* key of the record it came from, so its element type is all the return type
|
|
203
|
+
* can be built from. Going through a mapped type instead states the
|
|
204
|
+
* invariant that a value-only transform preserves the key set.
|
|
205
|
+
*
|
|
206
|
+
* **Key handling**: keys reach the callback the way `Object.entries` yields
|
|
207
|
+
* them, so a numeric key `1` arrives as `'1'`. Symbol-keyed properties are
|
|
208
|
+
* not enumerated by `Object.entries`; they are dropped from both the result
|
|
209
|
+
* and its type.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
*
|
|
213
|
+
* <!-- doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->
|
|
214
|
+
*
|
|
215
|
+
* ```ts
|
|
216
|
+
* const scores = { alice: 1, bob: 2 } as const;
|
|
217
|
+
*
|
|
218
|
+
* const doubled = Obj.map(scores, (value) => value * 2);
|
|
219
|
+
*
|
|
220
|
+
* assert.deepStrictEqual(doubled, { alice: 2, bob: 4 });
|
|
221
|
+
*
|
|
222
|
+
* // The key is available as the second argument.
|
|
223
|
+
* const labelled = Obj.map(scores, (value, key) => `${key}=${value}`);
|
|
224
|
+
*
|
|
225
|
+
* assert.deepStrictEqual(labelled, { alice: 'alice=1', bob: 'bob=2' });
|
|
226
|
+
* ```
|
|
227
|
+
*
|
|
228
|
+
* <!-- /doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->
|
|
229
|
+
*
|
|
230
|
+
* @template R - The type of the source record
|
|
231
|
+
* @template V2 - The type the callback returns for every key
|
|
232
|
+
* @param record - The record whose values are transformed
|
|
233
|
+
* @param mapFn - Called once per own enumerable string key, with the value
|
|
234
|
+
* and the key
|
|
235
|
+
* @returns A new record with the same keys as `record` and the mapped values
|
|
236
|
+
*/
|
|
237
|
+
const map: <const R extends UnknownRecord, V2>(record: R, mapFn: (value: R[keyof R], key: TsDataForgeInternals.MappedRecordKey<R>) => V2) => TsDataForgeInternals.MappedRecord<R, V2>;
|
|
192
238
|
/**
|
|
193
239
|
* Merges multiple records into a single record using `Object.assign`.
|
|
194
240
|
* Later records override properties from earlier records with the same key.
|
|
@@ -297,6 +343,19 @@ declare namespace TsDataForgeInternals {
|
|
|
297
343
|
type ValuesOfEntries<Entries extends readonly (readonly [PropertyKey, unknown])[]> = ValuesOfEntriesImpl<never, Entries, RecursionLimit>;
|
|
298
344
|
type ValuesOfEntriesImpl<K, Entries extends readonly (readonly [PropertyKey, unknown])[], RemainingNumRecursions extends number> = TypeEq<RemainingNumRecursions, 0> extends true ? K : TypeEq<Entries['length'], 0> extends true ? K : ValuesOfEntriesImpl<K | Entries[0][1], List.Tail<Entries>, Decrement<RemainingNumRecursions>>;
|
|
299
345
|
type PartialIfKeyIsUnion<K, T> = IsUnion<K> extends true ? Partial<T> : T;
|
|
346
|
+
/**
|
|
347
|
+
* The keys `Object.entries` yields for `R`: symbol keys are not enumerated,
|
|
348
|
+
* and a numeric key `1` arrives as the string `'1'`.
|
|
349
|
+
*/
|
|
350
|
+
type MappedRecordKey<R> = ToString<RelaxedExclude<keyof R, symbol>>;
|
|
351
|
+
/**
|
|
352
|
+
* `R` with every value replaced by `V2`. Homomorphic over `keyof R`, so
|
|
353
|
+
* optional modifiers and index signatures are preserved; the key remapping
|
|
354
|
+
* drops the symbol keys that `Object.entries` would not have visited.
|
|
355
|
+
*/
|
|
356
|
+
type MappedRecord<R, V2> = Readonly<{
|
|
357
|
+
[K in keyof R as K extends symbol ? never : K]: V2;
|
|
358
|
+
}>;
|
|
300
359
|
/**
|
|
301
360
|
* Merges two object types where keys in B override keys in A, preserving optional modifiers.
|
|
302
361
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.d.mts","sourceRoot":"","sources":["../../src/object/object.mts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"object.d.mts","sourceRoot":"","sources":["../../src/object/object.mts"],"names":[],"mappings":"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"}
|
package/dist/object/object.mjs
CHANGED
|
@@ -100,6 +100,20 @@ var Obj;
|
|
|
100
100
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
101
101
|
Object.fromEntries(entries)
|
|
102
102
|
);
|
|
103
|
+
Obj2.map = (record, mapFn) => {
|
|
104
|
+
const mapFnWidened = mapFn;
|
|
105
|
+
return (
|
|
106
|
+
// `Object.entries` enumerates exactly the own enumerable string keys of
|
|
107
|
+
// `record`, and nothing here adds or removes an entry, so the result has
|
|
108
|
+
// the key set `MappedRecord` claims.
|
|
109
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
110
|
+
Object.fromEntries(
|
|
111
|
+
Object.entries(record).map(
|
|
112
|
+
([k, v]) => [k, mapFnWidened(v, k)]
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
};
|
|
103
117
|
Obj2.merge = (...records) => (
|
|
104
118
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
105
119
|
Object.fromEntries(records.flatMap((r) => Object.entries(r)))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.mjs","sources":["../../src/object/object.mts"],"sourcesContent":["import {\n type ArrayElement,\n type Decrement,\n type DeepOmit,\n type DeepPick,\n type IsFixedLengthList,\n type IsUnion,\n type List,\n type ReadonlyRecord,\n type RelaxedExclude,\n type RelaxedOmit,\n type RelaxedPick,\n type StrictOmit,\n type StrictPick,\n type TypeEq,\n type UnknownRecord,\n} from 'ts-type-forge';\nimport { Arr } from '../array/index.mjs';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * A collection of type-safe object utility functions providing functional\n * programming patterns for object manipulation, including pick, omit, shallow\n * equality checks, and more.\n *\n * All functions maintain TypeScript type safety and support both direct and\n * curried usage patterns for better composition with pipe operations.\n */\nexport namespace Obj {\n /**\n * Performs a shallow equality check on two records using a configurable\n * equality function. Verifies that both records have the same number of\n * entries and that for every key in the first record, the corresponding value\n * passes the equality test with the value in the second record.\n *\n * @example\n *\n * ```ts\n * const obj1 = { name: 'Alice', age: 30 } as const;\n *\n * const obj2 = { name: 'Alice', age: 30 } as const;\n *\n * const obj3 = { name: 'Alice', age: 31 } as const;\n *\n * assert.isTrue(Obj.shallowEq(obj1, obj2));\n *\n * assert.isFalse(Obj.shallowEq(obj1, obj3));\n *\n * // Custom equality function\n * const obj4 = { value: 1 } as const;\n *\n * const obj5 = { value: 1.00001 } as const;\n *\n * const closeEnough = (a: unknown, b: unknown): boolean => {\n * if (typeof a === 'number' && typeof b === 'number') {\n * return Math.abs(a - b) < 0.001;\n * }\n *\n * return Object.is(a, b);\n * };\n *\n * assert.isTrue(Obj.shallowEq(obj4, obj5, closeEnough));\n * ```\n *\n * @param a - The first record to compare\n * @param b - The second record to compare\n * @param eq - Optional equality function (defaults to Object.is for strict\n * equality)\n * @returns `true` if the records are shallowly equal according to the\n * equality function, `false` otherwise\n */\n export const shallowEq = (\n a: UnknownRecord,\n b: UnknownRecord,\n eq: (x: unknown, y: unknown) => boolean = Object.is,\n ): boolean => {\n const aEntries = Object.entries(a);\n\n const bEntries = Object.entries(b);\n\n if (aEntries.length !== bEntries.length) return false;\n\n return aEntries.every(([k, v]) => eq(b[k], v));\n };\n\n /**\n * Creates a new record that contains only the specified keys from the source\n * record. This function supports both direct usage and curried form for\n * functional composition.\n *\n * **Type Safety**: Only keys that exist in the source record type are\n * allowed, preventing runtime errors from accessing non-existent properties.\n *\n * @example\n *\n * ```ts\n * const user = {\n * id: 1,\n * name: 'Bob',\n * email: 'bob@example.com',\n * password: 'secret',\n * role: 'admin',\n * } as const;\n *\n * // Direct usage\n * const publicInfo = Obj.pick(user, ['id', 'name', 'role']);\n *\n * assert.deepStrictEqual(publicInfo, {\n * id: 1,\n * name: 'Bob',\n * role: 'admin',\n * });\n *\n * // Curried usage with pipe\n * const nameAndEmail = pipe(user).map(Obj.pick(['name', 'email'])).value;\n *\n * assert.deepStrictEqual(nameAndEmail, {\n * name: 'Bob',\n * email: 'bob@example.com',\n * });\n * ```\n *\n * @template R - The type of the input record\n * @template Keys - The readonly array type of keys to pick from the record\n * @param record - The source record to pick properties from\n * @param keys - A readonly array of keys to include in the result\n * @returns A new record containing only the specified keys and their values\n */\n export function pick<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(record: R, keys: Keys): StrictPick<R, ArrayElement<Keys>>;\n\n // Curried version\n export function pick<const Keys extends readonly PropertyKey[]>(\n keys: Keys,\n ): <const R extends UnknownRecord>(\n record: R,\n ) => RelaxedPick<R, ArrayElement<Keys>>;\n\n export function pick<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(\n ...args: readonly [record: R, keys: Keys] | readonly [keys: Keys]\n ):\n | StrictPick<R, ArrayElement<Keys>>\n | ((record: R) => RelaxedPick<R, ArrayElement<Keys>>) {\n switch (args.length) {\n case 2: {\n const [record, keys] = args;\n\n const keysSet = new Set<keyof R>(keys);\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(\n Object.entries(record).filter(([k, _v]) => keysSet.has(k)),\n ) as never\n );\n }\n\n case 1: {\n const [keys] = args;\n\n return (record: R) => pick(record, keys);\n }\n }\n }\n\n /**\n * Creates a new record that excludes the specified keys from the source\n * record. This function supports both direct usage and curried form for\n * functional composition.\n *\n * **Type Safety**: Only keys that exist in the source record type are\n * allowed, and the return type precisely reflects which properties remain\n * after omission.\n *\n * @example\n *\n * ```ts\n * const user = {\n * id: 1,\n * name: 'Charlie',\n * email: 'charlie@example.com',\n * password: 'secret123',\n * internalNote: 'VIP customer',\n * } as const;\n *\n * // Direct usage - remove sensitive fields\n * const safeUser = Obj.omit(user, ['password', 'internalNote']);\n *\n * assert.deepStrictEqual(safeUser, {\n * id: 1,\n * name: 'Charlie',\n * email: 'charlie@example.com',\n * });\n *\n * // Curried usage with pipe\n * const withoutEmail = pipe(user).map(Obj.omit(['email', 'password'])).value;\n *\n * assert.deepStrictEqual(withoutEmail, {\n * id: 1,\n * name: 'Charlie',\n * internalNote: 'VIP customer',\n * });\n * ```\n *\n * @template R - The type of the input record\n * @template Keys - The readonly array type of keys to omit from the record\n * @param record - The source record to omit properties from\n * @param keys - A readonly array of keys to exclude from the result\n * @returns A new record containing all properties except the specified keys\n */\n export function omit<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(record: R, keys: Keys): StrictOmit<R, ArrayElement<Keys>>;\n\n // Curried version\n export function omit<const Keys extends readonly PropertyKey[]>(\n keys: Keys,\n ): <const R extends UnknownRecord>(\n record: R,\n ) => RelaxedOmit<R, ArrayElement<Keys>>;\n\n export function omit<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(\n ...args:\n readonly [record: R, keys: Keys] | readonly [keys: readonly PropertyKey[]]\n ):\n | StrictOmit<R, ArrayElement<Keys>>\n | ((record: R) => StrictOmit<R, ArrayElement<Keys>>) {\n switch (args.length) {\n case 2: {\n const [record, keys] = args;\n\n const keysSet = new Set<keyof R>(keys);\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(\n Object.entries(record).filter(([k, _v]) => !keysSet.has(k)),\n ) as never\n );\n }\n\n case 1: {\n const [keys] = args;\n\n return <R2 extends UnknownRecord>(record: R2) => {\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n const result = omit(\n record,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n keys as readonly (keyof R2)[],\n ) as RelaxedOmit<R2, ArrayElement<Keys>>;\n\n return result;\n };\n }\n }\n }\n\n /**\n * Creates an object from an array of key-value pairs with precise TypeScript\n * typing. This is a type-safe wrapper around `Object.fromEntries` that\n * provides better type inference and compile-time guarantees about the\n * resulting object structure.\n *\n * **Type Behavior**:\n *\n * - When entries is a fixed-length tuple, the exact object type is inferred\n * - When entries has dynamic length with union key types, `Partial` is applied\n * to prevent incorrect assumptions about which keys will be present\n *\n * @example\n *\n * ```ts\n * // Fixed-length tuple - exact type inferred\n * const entries1 = [\n * ['name', 'David'],\n * ['age', 25],\n * ['active', true],\n * ] as const;\n *\n * const obj1 = Obj.fromEntries(entries1);\n *\n * assert.deepStrictEqual(obj1, {\n * name: 'David',\n * age: 25,\n * active: true,\n * });\n *\n * // Dynamic length array - Partial type applied\n * const dynamicEntries: readonly (readonly ['x' | 'y', number])[] = [\n * ['x', 10],\n * ['y', 20],\n * ] as const;\n *\n * const obj2 = Obj.fromEntries(dynamicEntries);\n *\n * assert.deepStrictEqual(obj2, { x: 10, y: 20 });\n * ```\n *\n * @template Entries - The readonly array type of key-value entry tuples\n * @param entries - An array of readonly key-value entry tuples `[key, value]`\n * @returns An object created from the entries with precise typing\n */\n export const fromEntries = <\n const Entries extends readonly (readonly [PropertyKey, unknown])[],\n >(\n entries: Entries,\n ): IsFixedLengthList<Entries> extends true\n ? TsDataForgeInternals.EntriesToObject<Entries>\n : TsDataForgeInternals.PartialIfKeyIsUnion<\n TsDataForgeInternals.KeysOfEntries<Entries>,\n ReadonlyRecord<\n TsDataForgeInternals.KeysOfEntries<Entries>,\n TsDataForgeInternals.ValuesOfEntries<Entries>\n >\n > =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(entries) as never;\n\n /**\n * Merges multiple records into a single record using `Object.assign`.\n * Later records override properties from earlier records with the same key.\n *\n * @example\n *\n * <!-- doc:embed:jsdoc:example:./samples/src/object/merge-example.mts -->\n *\n * ```ts\n * const a = { a: 0, b: 0 } as const;\n *\n * const b = { b: 1, c: 0 } as const;\n *\n * const result = Obj.merge(a, b);\n *\n * assert.deepStrictEqual(result, { a: 0, b: 1, c: 0 });\n * ```\n *\n * <!-- /doc:embed:jsdoc:example:./samples/src/object/merge-example.mts -->\n *\n * @param records - The records to merge\n * @returns A new record with all properties merged\n */\n export const merge = <const Records extends readonly UnknownRecord[]>(\n ...records: Records\n ): TsDataForgeInternals.MergeAll<Records> =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(records.flatMap((r) => Object.entries(r))) as never;\n\n /**\n * Deeply picks a nested property from an object along the specified key path.\n * Supports both direct and curried usage.\n *\n * @example\n *\n * ```ts\n * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;\n *\n * // Direct usage\n * const result = Obj.deepPick(data, ['a', 'b', 'c']);\n *\n * assert.deepStrictEqual(result, { a: { b: { c: 1 } } });\n *\n * // Curried usage with pipe\n * const pickAB = Obj.deepPick(['a', 'b']);\n *\n * const result2 = pipe(data).map(pickAB).value;\n *\n * assert.deepStrictEqual(result2, { a: { b: { c: 1, d: 2 } } });\n * ```\n *\n * @template R - The type of the input record\n * @template Path - The key path tuple\n * @param record - The source record\n * @param path - A readonly tuple of keys representing the nested path\n * @returns A new record containing only the nested property at the path\n */\n export function deepPick<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(record: R, path: Path): DeepPick<R, Path>;\n\n // Curried version\n export function deepPick<const Path extends readonly (string | number)[]>(\n path: Path,\n ): <const R extends UnknownRecord>(record: R) => DeepPick<R, Path>;\n\n export function deepPick<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(\n ...args: readonly [record: R, path: Path] | readonly [path: Path]\n ): DeepPick<R, Path> | ((record: R) => DeepPick<R, Path>) {\n switch (args.length) {\n case 2: {\n const [record, path] = args;\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n deepPickImpl(record, path) as never\n );\n }\n\n case 1: {\n const [path] = args;\n\n return (record: R) => deepPick(record, path);\n }\n }\n }\n\n /**\n * Deeply omits a nested property from an object along the specified key path.\n * Supports both direct and curried usage.\n *\n * @example\n *\n * ```ts\n * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;\n *\n * // Direct usage\n * const result = Obj.deepOmit(data, ['a', 'b', 'c']);\n *\n * assert.deepStrictEqual(result, { a: { b: { d: 2 }, e: 3 }, f: 4 });\n *\n * // Curried usage with pipe\n * const omitAB = Obj.deepOmit(['a', 'b']);\n *\n * const result2 = pipe(data).map(omitAB).value;\n *\n * assert.deepStrictEqual(result2, { a: { e: 3 }, f: 4 });\n * ```\n *\n * @template R - The type of the input record\n * @template Path - The key path tuple\n * @param record - The source record\n * @param path - A readonly tuple of keys representing the nested path to omit\n * @returns A new record with the nested property at the path removed\n */\n export function deepOmit<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(record: R, path: Path): DeepOmit<R, Path>;\n\n // Curried version\n export function deepOmit<const Path extends readonly (string | number)[]>(\n path: Path,\n ): <const R extends UnknownRecord>(record: R) => DeepOmit<R, Path>;\n\n export function deepOmit<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(\n ...args: readonly [record: R, path: Path] | readonly [path: Path]\n ): DeepOmit<R, Path> | ((record: R) => DeepOmit<R, Path>) {\n switch (args.length) {\n case 2: {\n const [record, path] = args;\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n deepOmitImpl(record, path) as never\n );\n }\n\n case 1: {\n const [path] = args;\n\n return (record: R) => deepOmit(record, path);\n }\n }\n }\n}\n\nconst deepPickImpl = (\n record: UnknownRecord,\n path: readonly (string | number)[],\n): UnknownRecord => {\n if (!Arr.isNonEmpty(path)) return record;\n\n const head = path[0];\n\n if (!hasKey(record, head)) return {};\n\n const value = record[head];\n\n const tail = path.slice(1);\n\n if (!Arr.isNonEmpty(tail)) return { [head]: value };\n\n if (!isRecord(value)) return { [head]: {} };\n\n return {\n [head]: deepPickImpl(value, tail),\n };\n};\n\nconst deepOmitImpl = (\n record: UnknownRecord,\n path: readonly (string | number)[],\n): UnknownRecord => {\n if (!Arr.isNonEmpty(path)) return record;\n\n const head = path[0];\n\n if (!hasKey(record, head)) return record;\n\n const tail = path.slice(1);\n\n if (!Arr.isNonEmpty(tail)) {\n return Object.fromEntries(\n Object.entries(record).filter(([k]) => k !== head),\n );\n }\n\n const value = record[head];\n\n if (!isRecord(value)) return record;\n\n return {\n ...record,\n [head]: deepOmitImpl(value, tail),\n };\n};\n\n/**\n * @internal\n * Internal type utilities for the Obj module.\n */\ndeclare namespace TsDataForgeInternals {\n type RecursionLimit = 20;\n\n /** - `[['x', 1], ['y', 3]]` -> `{ x: 1, y: 3 }` */\n export type EntriesToObject<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n > = Readonly<EntriesToObjectImpl<{}, Entries>>;\n\n /** @internal */\n type EntriesToObjectImpl<\n R,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > =\n TypeEq<Entries['length'], 0> extends true\n ? R\n : EntriesToObjectImpl<\n R & ReadonlyRecord<Entries[0][0], Entries[0][1]>,\n List.Tail<Entries>\n >;\n\n /**\n * - `['x' | 'y' | 'z', number][]]` -> `'x' | 'y' | 'z'`\n * - `[['a' | 'b' | 'c', number], ...['x' | 'y' | 'z', number][]]` -> `'a' |\n * 'b' | 'c' | 'x' | 'y' | 'z'`\n *\n * @note To handle the second example above, recursion needs to be performed on infinite-length Entries,\n * but since the timing to stop cannot be determined, a recursion limit is set.\n */\n export type KeysOfEntries<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > = KeysOfEntriesImpl<never, Entries, RecursionLimit>;\n\n type KeysOfEntriesImpl<\n K,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n RemainingNumRecursions extends number,\n > =\n TypeEq<RemainingNumRecursions, 0> extends true\n ? K\n : TypeEq<Entries['length'], 0> extends true\n ? K\n : KeysOfEntriesImpl<\n K | Entries[0][0],\n List.Tail<Entries>,\n Decrement<RemainingNumRecursions>\n >;\n\n export type ValuesOfEntries<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > = ValuesOfEntriesImpl<never, Entries, RecursionLimit>;\n\n type ValuesOfEntriesImpl<\n K,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n RemainingNumRecursions extends number,\n > =\n TypeEq<RemainingNumRecursions, 0> extends true\n ? K\n : TypeEq<Entries['length'], 0> extends true\n ? K\n : ValuesOfEntriesImpl<\n K | Entries[0][1],\n List.Tail<Entries>,\n Decrement<RemainingNumRecursions>\n >;\n\n export type PartialIfKeyIsUnion<K, T> =\n IsUnion<K> extends true ? Partial<T> : T;\n\n /**\n * Merges two object types where keys in B override keys in A, preserving optional modifiers.\n *\n * This implementation uses a sophisticated technique to preserve optional property modifiers (`?`)\n * during the merge operation, which would otherwise be lost in a naive mapped type implementation.\n *\n * ## Core Technique: Optional Property Detection\n *\n * Uses `{} extends Pick<T, K>` to determine if a property is optional:\n * - **Required property**: `Pick<T, 'x'>` = `{ x: number }` → `{}` does NOT extend it (false)\n * - **Optional property**: `Pick<T, 'y'>` = `{ y?: string }` → `{}` DOES extend it (true)\n *\n * This works because `{}` (empty object) is assignable to objects with only optional properties\n * but not to objects with required properties.\n *\n * ## Implementation Strategy\n *\n * The type is constructed as an intersection of five mapped types:\n *\n * 1. **Required properties from B** - Properties in B that are required (override A completely)\n * 2. **Optional properties from B (not in A)** - New optional properties from B\n * 3. **Optional properties from B (in A)** - Union of A[K] | B[K] to preserve both possibilities\n * 4. **Required properties only in A** - Properties in A but not in B that are required\n * 5. **Optional properties only in A** - Properties in A but not in B that are optional (with `?` modifier)\n *\n * Finally, the intersection is flattened using an optional-preserving identity mapping that\n * re-applies the optional detection logic to maintain the `?` modifiers in the final type.\n *\n * @example\n * ```ts\n * type A = Readonly<{ x: number; y?: string }>;\n *\n * type B = Readonly<{ y?: number; z?: boolean }>;\n *\n * type Result = MergeTwo<A, B>;\n * // Result: Readonly<{ x: number; y?: string | number; z?: boolean }>\n * ```\n */\n // transformer-ignore-next-line\n type MergeTwo<A extends UnknownRecord, B extends UnknownRecord> = {\n // 1. Required properties from B (override A completely)\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n readonly [K in keyof B as {} extends StrictPick<B, K> ? never : K]: B[K];\n } & {\n // 2. Optional properties from B that are NOT in A\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in keyof B as {} extends StrictPick<B, K>\n ? K extends keyof A\n ? never\n : K\n : never\n ]?: B[K];\n } & {\n // 3. Optional properties from B that ARE in A (create union)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in keyof B as {} extends StrictPick<B, K>\n ? K extends keyof A\n ? K\n : never\n : never\n ]?: K extends keyof A ? A[K] | B[K] : never;\n } & {\n // 4. Required properties only in A (not overridden by B)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>\n ? never\n : K\n ]: A[K];\n } & {\n // 5. Optional properties only in A (not overridden by B)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>\n ? K\n : never\n ]?: A[K];\n } extends infer O\n ? Readonly<\n {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n [K in keyof O as {} extends StrictPick<O, K> ? never : K]: O[K];\n } & {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n [K in keyof O as {} extends StrictPick<O, K> ? K : never]?: O[K];\n }\n >\n : never;\n\n /** Sequentially merges a tuple of object types from left to right. */\n export type MergeAll<\n Records extends readonly UnknownRecord[],\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n Acc extends UnknownRecord = {},\n > =\n IsFixedLengthList<Records> extends false\n ? ArrayElement<Records>\n : Records extends readonly [\n infer First extends UnknownRecord,\n ...infer Rest extends readonly UnknownRecord[],\n ]\n ? MergeAll<Rest, MergeTwo<Acc, First>>\n : Acc;\n}\n"],"names":["Obj","Arr.isNonEmpty"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"object.mjs","sources":["../../src/object/object.mts"],"sourcesContent":["import {\n type ArrayElement,\n type Decrement,\n type DeepOmit,\n type DeepPick,\n type IsFixedLengthList,\n type IsUnion,\n type List,\n type ReadonlyRecord,\n type RelaxedExclude,\n type RelaxedOmit,\n type RelaxedPick,\n type StrictOmit,\n type StrictPick,\n type ToString,\n type TypeEq,\n type UnknownRecord,\n} from 'ts-type-forge';\nimport { Arr } from '../array/index.mjs';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * A collection of type-safe object utility functions providing functional\n * programming patterns for object manipulation, including pick, omit, shallow\n * equality checks, and more.\n *\n * All functions maintain TypeScript type safety and support both direct and\n * curried usage patterns for better composition with pipe operations.\n */\nexport namespace Obj {\n /**\n * Performs a shallow equality check on two records using a configurable\n * equality function. Verifies that both records have the same number of\n * entries and that for every key in the first record, the corresponding value\n * passes the equality test with the value in the second record.\n *\n * @example\n *\n * ```ts\n * const obj1 = { name: 'Alice', age: 30 } as const;\n *\n * const obj2 = { name: 'Alice', age: 30 } as const;\n *\n * const obj3 = { name: 'Alice', age: 31 } as const;\n *\n * assert.isTrue(Obj.shallowEq(obj1, obj2));\n *\n * assert.isFalse(Obj.shallowEq(obj1, obj3));\n *\n * // Custom equality function\n * const obj4 = { value: 1 } as const;\n *\n * const obj5 = { value: 1.00001 } as const;\n *\n * const closeEnough = (a: unknown, b: unknown): boolean => {\n * if (typeof a === 'number' && typeof b === 'number') {\n * return Math.abs(a - b) < 0.001;\n * }\n *\n * return Object.is(a, b);\n * };\n *\n * assert.isTrue(Obj.shallowEq(obj4, obj5, closeEnough));\n * ```\n *\n * @param a - The first record to compare\n * @param b - The second record to compare\n * @param eq - Optional equality function (defaults to Object.is for strict\n * equality)\n * @returns `true` if the records are shallowly equal according to the\n * equality function, `false` otherwise\n */\n export const shallowEq = (\n a: UnknownRecord,\n b: UnknownRecord,\n eq: (x: unknown, y: unknown) => boolean = Object.is,\n ): boolean => {\n const aEntries = Object.entries(a);\n\n const bEntries = Object.entries(b);\n\n if (aEntries.length !== bEntries.length) return false;\n\n return aEntries.every(([k, v]) => eq(b[k], v));\n };\n\n /**\n * Creates a new record that contains only the specified keys from the source\n * record. This function supports both direct usage and curried form for\n * functional composition.\n *\n * **Type Safety**: Only keys that exist in the source record type are\n * allowed, preventing runtime errors from accessing non-existent properties.\n *\n * @example\n *\n * ```ts\n * const user = {\n * id: 1,\n * name: 'Bob',\n * email: 'bob@example.com',\n * password: 'secret',\n * role: 'admin',\n * } as const;\n *\n * // Direct usage\n * const publicInfo = Obj.pick(user, ['id', 'name', 'role']);\n *\n * assert.deepStrictEqual(publicInfo, {\n * id: 1,\n * name: 'Bob',\n * role: 'admin',\n * });\n *\n * // Curried usage with pipe\n * const nameAndEmail = pipe(user).map(Obj.pick(['name', 'email'])).value;\n *\n * assert.deepStrictEqual(nameAndEmail, {\n * name: 'Bob',\n * email: 'bob@example.com',\n * });\n * ```\n *\n * @template R - The type of the input record\n * @template Keys - The readonly array type of keys to pick from the record\n * @param record - The source record to pick properties from\n * @param keys - A readonly array of keys to include in the result\n * @returns A new record containing only the specified keys and their values\n */\n export function pick<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(record: R, keys: Keys): StrictPick<R, ArrayElement<Keys>>;\n\n // Curried version\n export function pick<const Keys extends readonly PropertyKey[]>(\n keys: Keys,\n ): <const R extends UnknownRecord>(\n record: R,\n ) => RelaxedPick<R, ArrayElement<Keys>>;\n\n export function pick<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(\n ...args: readonly [record: R, keys: Keys] | readonly [keys: Keys]\n ):\n | StrictPick<R, ArrayElement<Keys>>\n | ((record: R) => RelaxedPick<R, ArrayElement<Keys>>) {\n switch (args.length) {\n case 2: {\n const [record, keys] = args;\n\n const keysSet = new Set<keyof R>(keys);\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(\n Object.entries(record).filter(([k, _v]) => keysSet.has(k)),\n ) as never\n );\n }\n\n case 1: {\n const [keys] = args;\n\n return (record: R) => pick(record, keys);\n }\n }\n }\n\n /**\n * Creates a new record that excludes the specified keys from the source\n * record. This function supports both direct usage and curried form for\n * functional composition.\n *\n * **Type Safety**: Only keys that exist in the source record type are\n * allowed, and the return type precisely reflects which properties remain\n * after omission.\n *\n * @example\n *\n * ```ts\n * const user = {\n * id: 1,\n * name: 'Charlie',\n * email: 'charlie@example.com',\n * password: 'secret123',\n * internalNote: 'VIP customer',\n * } as const;\n *\n * // Direct usage - remove sensitive fields\n * const safeUser = Obj.omit(user, ['password', 'internalNote']);\n *\n * assert.deepStrictEqual(safeUser, {\n * id: 1,\n * name: 'Charlie',\n * email: 'charlie@example.com',\n * });\n *\n * // Curried usage with pipe\n * const withoutEmail = pipe(user).map(Obj.omit(['email', 'password'])).value;\n *\n * assert.deepStrictEqual(withoutEmail, {\n * id: 1,\n * name: 'Charlie',\n * internalNote: 'VIP customer',\n * });\n * ```\n *\n * @template R - The type of the input record\n * @template Keys - The readonly array type of keys to omit from the record\n * @param record - The source record to omit properties from\n * @param keys - A readonly array of keys to exclude from the result\n * @returns A new record containing all properties except the specified keys\n */\n export function omit<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(record: R, keys: Keys): StrictOmit<R, ArrayElement<Keys>>;\n\n // Curried version\n export function omit<const Keys extends readonly PropertyKey[]>(\n keys: Keys,\n ): <const R extends UnknownRecord>(\n record: R,\n ) => RelaxedOmit<R, ArrayElement<Keys>>;\n\n export function omit<\n const R extends UnknownRecord,\n const Keys extends readonly (keyof R)[],\n >(\n ...args:\n readonly [record: R, keys: Keys] | readonly [keys: readonly PropertyKey[]]\n ):\n | StrictOmit<R, ArrayElement<Keys>>\n | ((record: R) => StrictOmit<R, ArrayElement<Keys>>) {\n switch (args.length) {\n case 2: {\n const [record, keys] = args;\n\n const keysSet = new Set<keyof R>(keys);\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(\n Object.entries(record).filter(([k, _v]) => !keysSet.has(k)),\n ) as never\n );\n }\n\n case 1: {\n const [keys] = args;\n\n return <R2 extends UnknownRecord>(record: R2) => {\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n const result = omit(\n record,\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n keys as readonly (keyof R2)[],\n ) as RelaxedOmit<R2, ArrayElement<Keys>>;\n\n return result;\n };\n }\n }\n }\n\n /**\n * Creates an object from an array of key-value pairs with precise TypeScript\n * typing. This is a type-safe wrapper around `Object.fromEntries` that\n * provides better type inference and compile-time guarantees about the\n * resulting object structure.\n *\n * **Type Behavior**:\n *\n * - When entries is a fixed-length tuple, the exact object type is inferred\n * - When entries has dynamic length with union key types, `Partial` is applied\n * to prevent incorrect assumptions about which keys will be present\n *\n * @example\n *\n * ```ts\n * // Fixed-length tuple - exact type inferred\n * const entries1 = [\n * ['name', 'David'],\n * ['age', 25],\n * ['active', true],\n * ] as const;\n *\n * const obj1 = Obj.fromEntries(entries1);\n *\n * assert.deepStrictEqual(obj1, {\n * name: 'David',\n * age: 25,\n * active: true,\n * });\n *\n * // Dynamic length array - Partial type applied\n * const dynamicEntries: readonly (readonly ['x' | 'y', number])[] = [\n * ['x', 10],\n * ['y', 20],\n * ] as const;\n *\n * const obj2 = Obj.fromEntries(dynamicEntries);\n *\n * assert.deepStrictEqual(obj2, { x: 10, y: 20 });\n * ```\n *\n * @template Entries - The readonly array type of key-value entry tuples\n * @param entries - An array of readonly key-value entry tuples `[key, value]`\n * @returns An object created from the entries with precise typing\n */\n export const fromEntries = <\n const Entries extends readonly (readonly [PropertyKey, unknown])[],\n >(\n entries: Entries,\n ): IsFixedLengthList<Entries> extends true\n ? TsDataForgeInternals.EntriesToObject<Entries>\n : TsDataForgeInternals.PartialIfKeyIsUnion<\n TsDataForgeInternals.KeysOfEntries<Entries>,\n ReadonlyRecord<\n TsDataForgeInternals.KeysOfEntries<Entries>,\n TsDataForgeInternals.ValuesOfEntries<Entries>\n >\n > =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(entries) as never;\n\n /**\n * Transforms every value of a record, leaving the key set untouched.\n *\n * **Type Behavior**: the result is a mapped type over `keyof R`, so the keys\n * — and their optional modifiers — survive verbatim:\n * `{ a: number; b?: number }` maps to `{ a: V2; b?: V2 }`, and a record with\n * an index signature stays total rather than becoming `Partial`.\n *\n * This is what `Object.fromEntries(Object.entries(record).map(...))` cannot\n * express: an entries array carries no evidence that it still covers every\n * key of the record it came from, so its element type is all the return type\n * can be built from. Going through a mapped type instead states the\n * invariant that a value-only transform preserves the key set.\n *\n * **Key handling**: keys reach the callback the way `Object.entries` yields\n * them, so a numeric key `1` arrives as `'1'`. Symbol-keyed properties are\n * not enumerated by `Object.entries`; they are dropped from both the result\n * and its type.\n *\n * @example\n *\n * <!-- doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->\n *\n * ```ts\n * const scores = { alice: 1, bob: 2 } as const;\n *\n * const doubled = Obj.map(scores, (value) => value * 2);\n *\n * assert.deepStrictEqual(doubled, { alice: 2, bob: 4 });\n *\n * // The key is available as the second argument.\n * const labelled = Obj.map(scores, (value, key) => `${key}=${value}`);\n *\n * assert.deepStrictEqual(labelled, { alice: 'alice=1', bob: 'bob=2' });\n * ```\n *\n * <!-- /doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->\n *\n * @template R - The type of the source record\n * @template V2 - The type the callback returns for every key\n * @param record - The record whose values are transformed\n * @param mapFn - Called once per own enumerable string key, with the value\n * and the key\n * @returns A new record with the same keys as `record` and the mapped values\n */\n export const map = <const R extends UnknownRecord, V2>(\n record: R,\n mapFn: (\n value: R[keyof R],\n key: TsDataForgeInternals.MappedRecordKey<R>,\n ) => V2,\n ): TsDataForgeInternals.MappedRecord<R, V2> => {\n // `Object.entries` erases both the key and the value type of `R`, so the\n // callback is widened once here instead of asserting on every entry.\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n const mapFnWidened = mapFn as (value: unknown, key: string) => V2;\n\n return (\n // `Object.entries` enumerates exactly the own enumerable string keys of\n // `record`, and nothing here adds or removes an entry, so the result has\n // the key set `MappedRecord` claims.\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(\n Object.entries(record).map(\n ([k, v]) => [k, mapFnWidened(v, k)] as const,\n ),\n ) as never\n );\n };\n\n /**\n * Merges multiple records into a single record using `Object.assign`.\n * Later records override properties from earlier records with the same key.\n *\n * @example\n *\n * <!-- doc:embed:jsdoc:example:./samples/src/object/merge-example.mts -->\n *\n * ```ts\n * const a = { a: 0, b: 0 } as const;\n *\n * const b = { b: 1, c: 0 } as const;\n *\n * const result = Obj.merge(a, b);\n *\n * assert.deepStrictEqual(result, { a: 0, b: 1, c: 0 });\n * ```\n *\n * <!-- /doc:embed:jsdoc:example:./samples/src/object/merge-example.mts -->\n *\n * @param records - The records to merge\n * @returns A new record with all properties merged\n */\n export const merge = <const Records extends readonly UnknownRecord[]>(\n ...records: Records\n ): TsDataForgeInternals.MergeAll<Records> =>\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n Object.fromEntries(records.flatMap((r) => Object.entries(r))) as never;\n\n /**\n * Deeply picks a nested property from an object along the specified key path.\n * Supports both direct and curried usage.\n *\n * @example\n *\n * ```ts\n * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;\n *\n * // Direct usage\n * const result = Obj.deepPick(data, ['a', 'b', 'c']);\n *\n * assert.deepStrictEqual(result, { a: { b: { c: 1 } } });\n *\n * // Curried usage with pipe\n * const pickAB = Obj.deepPick(['a', 'b']);\n *\n * const result2 = pipe(data).map(pickAB).value;\n *\n * assert.deepStrictEqual(result2, { a: { b: { c: 1, d: 2 } } });\n * ```\n *\n * @template R - The type of the input record\n * @template Path - The key path tuple\n * @param record - The source record\n * @param path - A readonly tuple of keys representing the nested path\n * @returns A new record containing only the nested property at the path\n */\n export function deepPick<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(record: R, path: Path): DeepPick<R, Path>;\n\n // Curried version\n export function deepPick<const Path extends readonly (string | number)[]>(\n path: Path,\n ): <const R extends UnknownRecord>(record: R) => DeepPick<R, Path>;\n\n export function deepPick<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(\n ...args: readonly [record: R, path: Path] | readonly [path: Path]\n ): DeepPick<R, Path> | ((record: R) => DeepPick<R, Path>) {\n switch (args.length) {\n case 2: {\n const [record, path] = args;\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n deepPickImpl(record, path) as never\n );\n }\n\n case 1: {\n const [path] = args;\n\n return (record: R) => deepPick(record, path);\n }\n }\n }\n\n /**\n * Deeply omits a nested property from an object along the specified key path.\n * Supports both direct and curried usage.\n *\n * @example\n *\n * ```ts\n * const data = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 } as const;\n *\n * // Direct usage\n * const result = Obj.deepOmit(data, ['a', 'b', 'c']);\n *\n * assert.deepStrictEqual(result, { a: { b: { d: 2 }, e: 3 }, f: 4 });\n *\n * // Curried usage with pipe\n * const omitAB = Obj.deepOmit(['a', 'b']);\n *\n * const result2 = pipe(data).map(omitAB).value;\n *\n * assert.deepStrictEqual(result2, { a: { e: 3 }, f: 4 });\n * ```\n *\n * @template R - The type of the input record\n * @template Path - The key path tuple\n * @param record - The source record\n * @param path - A readonly tuple of keys representing the nested path to omit\n * @returns A new record with the nested property at the path removed\n */\n export function deepOmit<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(record: R, path: Path): DeepOmit<R, Path>;\n\n // Curried version\n export function deepOmit<const Path extends readonly (string | number)[]>(\n path: Path,\n ): <const R extends UnknownRecord>(record: R) => DeepOmit<R, Path>;\n\n export function deepOmit<\n const R extends UnknownRecord,\n const Path extends readonly (string | number)[],\n >(\n ...args: readonly [record: R, path: Path] | readonly [path: Path]\n ): DeepOmit<R, Path> | ((record: R) => DeepOmit<R, Path>) {\n switch (args.length) {\n case 2: {\n const [record, path] = args;\n\n return (\n // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n deepOmitImpl(record, path) as never\n );\n }\n\n case 1: {\n const [path] = args;\n\n return (record: R) => deepOmit(record, path);\n }\n }\n }\n}\n\nconst deepPickImpl = (\n record: UnknownRecord,\n path: readonly (string | number)[],\n): UnknownRecord => {\n if (!Arr.isNonEmpty(path)) return record;\n\n const head = path[0];\n\n if (!hasKey(record, head)) return {};\n\n const value = record[head];\n\n const tail = path.slice(1);\n\n if (!Arr.isNonEmpty(tail)) return { [head]: value };\n\n if (!isRecord(value)) return { [head]: {} };\n\n return {\n [head]: deepPickImpl(value, tail),\n };\n};\n\nconst deepOmitImpl = (\n record: UnknownRecord,\n path: readonly (string | number)[],\n): UnknownRecord => {\n if (!Arr.isNonEmpty(path)) return record;\n\n const head = path[0];\n\n if (!hasKey(record, head)) return record;\n\n const tail = path.slice(1);\n\n if (!Arr.isNonEmpty(tail)) {\n return Object.fromEntries(\n Object.entries(record).filter(([k]) => k !== head),\n );\n }\n\n const value = record[head];\n\n if (!isRecord(value)) return record;\n\n return {\n ...record,\n [head]: deepOmitImpl(value, tail),\n };\n};\n\n/**\n * @internal\n * Internal type utilities for the Obj module.\n */\ndeclare namespace TsDataForgeInternals {\n type RecursionLimit = 20;\n\n /** - `[['x', 1], ['y', 3]]` -> `{ x: 1, y: 3 }` */\n export type EntriesToObject<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n > = Readonly<EntriesToObjectImpl<{}, Entries>>;\n\n /** @internal */\n type EntriesToObjectImpl<\n R,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > =\n TypeEq<Entries['length'], 0> extends true\n ? R\n : EntriesToObjectImpl<\n R & ReadonlyRecord<Entries[0][0], Entries[0][1]>,\n List.Tail<Entries>\n >;\n\n /**\n * - `['x' | 'y' | 'z', number][]]` -> `'x' | 'y' | 'z'`\n * - `[['a' | 'b' | 'c', number], ...['x' | 'y' | 'z', number][]]` -> `'a' |\n * 'b' | 'c' | 'x' | 'y' | 'z'`\n *\n * @note To handle the second example above, recursion needs to be performed on infinite-length Entries,\n * but since the timing to stop cannot be determined, a recursion limit is set.\n */\n export type KeysOfEntries<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > = KeysOfEntriesImpl<never, Entries, RecursionLimit>;\n\n type KeysOfEntriesImpl<\n K,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n RemainingNumRecursions extends number,\n > =\n TypeEq<RemainingNumRecursions, 0> extends true\n ? K\n : TypeEq<Entries['length'], 0> extends true\n ? K\n : KeysOfEntriesImpl<\n K | Entries[0][0],\n List.Tail<Entries>,\n Decrement<RemainingNumRecursions>\n >;\n\n export type ValuesOfEntries<\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n > = ValuesOfEntriesImpl<never, Entries, RecursionLimit>;\n\n type ValuesOfEntriesImpl<\n K,\n Entries extends readonly (readonly [PropertyKey, unknown])[],\n RemainingNumRecursions extends number,\n > =\n TypeEq<RemainingNumRecursions, 0> extends true\n ? K\n : TypeEq<Entries['length'], 0> extends true\n ? K\n : ValuesOfEntriesImpl<\n K | Entries[0][1],\n List.Tail<Entries>,\n Decrement<RemainingNumRecursions>\n >;\n\n export type PartialIfKeyIsUnion<K, T> =\n IsUnion<K> extends true ? Partial<T> : T;\n\n /**\n * The keys `Object.entries` yields for `R`: symbol keys are not enumerated,\n * and a numeric key `1` arrives as the string `'1'`.\n */\n export type MappedRecordKey<R> = ToString<RelaxedExclude<keyof R, symbol>>;\n\n /**\n * `R` with every value replaced by `V2`. Homomorphic over `keyof R`, so\n * optional modifiers and index signatures are preserved; the key remapping\n * drops the symbol keys that `Object.entries` would not have visited.\n */\n export type MappedRecord<R, V2> = Readonly<{\n [K in keyof R as K extends symbol ? never : K]: V2;\n }>;\n\n /**\n * Merges two object types where keys in B override keys in A, preserving optional modifiers.\n *\n * This implementation uses a sophisticated technique to preserve optional property modifiers (`?`)\n * during the merge operation, which would otherwise be lost in a naive mapped type implementation.\n *\n * ## Core Technique: Optional Property Detection\n *\n * Uses `{} extends Pick<T, K>` to determine if a property is optional:\n * - **Required property**: `Pick<T, 'x'>` = `{ x: number }` → `{}` does NOT extend it (false)\n * - **Optional property**: `Pick<T, 'y'>` = `{ y?: string }` → `{}` DOES extend it (true)\n *\n * This works because `{}` (empty object) is assignable to objects with only optional properties\n * but not to objects with required properties.\n *\n * ## Implementation Strategy\n *\n * The type is constructed as an intersection of five mapped types:\n *\n * 1. **Required properties from B** - Properties in B that are required (override A completely)\n * 2. **Optional properties from B (not in A)** - New optional properties from B\n * 3. **Optional properties from B (in A)** - Union of A[K] | B[K] to preserve both possibilities\n * 4. **Required properties only in A** - Properties in A but not in B that are required\n * 5. **Optional properties only in A** - Properties in A but not in B that are optional (with `?` modifier)\n *\n * Finally, the intersection is flattened using an optional-preserving identity mapping that\n * re-applies the optional detection logic to maintain the `?` modifiers in the final type.\n *\n * @example\n * ```ts\n * type A = Readonly<{ x: number; y?: string }>;\n *\n * type B = Readonly<{ y?: number; z?: boolean }>;\n *\n * type Result = MergeTwo<A, B>;\n * // Result: Readonly<{ x: number; y?: string | number; z?: boolean }>\n * ```\n */\n // transformer-ignore-next-line\n type MergeTwo<A extends UnknownRecord, B extends UnknownRecord> = {\n // 1. Required properties from B (override A completely)\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n readonly [K in keyof B as {} extends StrictPick<B, K> ? never : K]: B[K];\n } & {\n // 2. Optional properties from B that are NOT in A\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in keyof B as {} extends StrictPick<B, K>\n ? K extends keyof A\n ? never\n : K\n : never\n ]?: B[K];\n } & {\n // 3. Optional properties from B that ARE in A (create union)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in keyof B as {} extends StrictPick<B, K>\n ? K extends keyof A\n ? K\n : never\n : never\n ]?: K extends keyof A ? A[K] | B[K] : never;\n } & {\n // 4. Required properties only in A (not overridden by B)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>\n ? never\n : K\n ]: A[K];\n } & {\n // 5. Optional properties only in A (not overridden by B)\n readonly [\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K>\n ? K\n : never\n ]?: A[K];\n } extends infer O\n ? Readonly<\n {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n [K in keyof O as {} extends StrictPick<O, K> ? never : K]: O[K];\n } & {\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n [K in keyof O as {} extends StrictPick<O, K> ? K : never]?: O[K];\n }\n >\n : never;\n\n /** Sequentially merges a tuple of object types from left to right. */\n export type MergeAll<\n Records extends readonly UnknownRecord[],\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n Acc extends UnknownRecord = {},\n > =\n IsFixedLengthList<Records> extends false\n ? ArrayElement<Records>\n : Records extends readonly [\n infer First extends UnknownRecord,\n ...infer Rest extends readonly UnknownRecord[],\n ]\n ? MergeAll<Rest, MergeTwo<Acc, First>>\n : Acc;\n}\n"],"names":["Obj","Arr.isNonEmpty"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-data-forge",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
],
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/noshiro-pf/
|
|
15
|
+
"url": "git+https://github.com/noshiro-pf/mono.git",
|
|
16
|
+
"directory": "libs/ts-data-forge"
|
|
16
17
|
},
|
|
17
18
|
"license": "Apache-2.0",
|
|
18
19
|
"author": "noshiro-pf <noshiro.pf@gmail.com>",
|
|
@@ -36,17 +37,29 @@
|
|
|
36
37
|
],
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@sindresorhus/is": "^8.1.0",
|
|
39
|
-
"ts-type-forge": "~9.1
|
|
40
|
+
"ts-type-forge": "~9.2.1"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@emotion/react": "11.14.0",
|
|
43
44
|
"@emotion/styled": "11.14.1",
|
|
44
|
-
"@mui/material": "9.
|
|
45
|
+
"@mui/material": "9.3.1",
|
|
45
46
|
"@types/react": "19.2.18",
|
|
46
|
-
"
|
|
47
|
+
"@vitest/browser-playwright": "4.1.10",
|
|
48
|
+
"dedent": "1.7.2",
|
|
49
|
+
"immer": "11.1.16",
|
|
50
|
+
"playwright": "1.62.1",
|
|
51
|
+
"prettier": "3.9.6",
|
|
52
|
+
"prettier-plugin-organize-imports": "4.3.0",
|
|
53
|
+
"prettier-plugin-packagejson": "3.0.2",
|
|
47
54
|
"react": "19.2.8",
|
|
55
|
+
"rollup": "4.62.4",
|
|
48
56
|
"typedoc": "0.28.20",
|
|
49
|
-
"typedoc-github-theme": "0.4.0"
|
|
57
|
+
"typedoc-github-theme": "0.4.0",
|
|
58
|
+
"vite": "8.2.1",
|
|
59
|
+
"eslint-config-typed": "5.9.2",
|
|
60
|
+
"eslint-plugin-ts-fortress": "1.0.3",
|
|
61
|
+
"eslint-plugin-ts-type-forge": "0.6.5",
|
|
62
|
+
"ts-repo-utils": "10.2.0"
|
|
50
63
|
},
|
|
51
64
|
"peerDependencies": {
|
|
52
65
|
"typescript": ">=4.8"
|
|
@@ -60,20 +73,20 @@
|
|
|
60
73
|
"provenance": true
|
|
61
74
|
},
|
|
62
75
|
"scripts": {
|
|
63
|
-
"build": "tsx ./scripts/cmd/build.mts",
|
|
64
|
-
"build:min": "tsx ./scripts/cmd/build.mts --skip-check",
|
|
65
|
-
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
66
|
-
"doc": "tsx ./scripts/cmd/gen-docs.mts",
|
|
67
|
-
"doc:embed": "tsx ./scripts/cmd/embed-examples.mts",
|
|
68
|
-
"doc:embed:jsdoc": "tsx ./scripts/cmd/embed-examples-in-jsdoc.mts",
|
|
76
|
+
"build": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts",
|
|
77
|
+
"build:min": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts --skip-check",
|
|
78
|
+
"check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
|
|
79
|
+
"doc": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/gen-docs.mts",
|
|
80
|
+
"doc:embed": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples.mts",
|
|
81
|
+
"doc:embed:jsdoc": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples-in-jsdoc.mts",
|
|
69
82
|
"doc:preview": "vite preview --config ./configs/vite.doc.config.mts",
|
|
70
83
|
"doc:watch": "typedoc --options ./configs/typedoc.config.mjs --watch",
|
|
71
|
-
"fmt": "format-uncommitted --cwd .",
|
|
72
|
-
"fmt:diff": "format-diff-from origin/main --cwd .",
|
|
84
|
+
"fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
|
|
85
|
+
"fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
|
|
73
86
|
"fmt:full": "pnpm run z:prettier .",
|
|
74
|
-
"gen:branded-number": "tsx ./scripts/gen-branded-number/generate.mts && pnpm run doc:embed:jsdoc && pnpm run z:prettier 'src/number/branded-types/*.mts' 'src/number/enum/*.mts'",
|
|
87
|
+
"gen:branded-number": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/gen-branded-number/generate.mts && pnpm run doc:embed:jsdoc && pnpm run z:prettier 'src/number/branded-types/*.mts' 'src/number/enum/*.mts'",
|
|
75
88
|
"gi": "run-s gi:src fmt",
|
|
76
|
-
"gi:src": "tsx scripts/cmd/gen-index.mts",
|
|
89
|
+
"gi:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json scripts/cmd/gen-index.mts",
|
|
77
90
|
"lint": "eslint .",
|
|
78
91
|
"lint:fix": "eslint . --fix",
|
|
79
92
|
"test": "pnpm run z:vitest:node run",
|
|
@@ -51,7 +51,6 @@ export type Stack<T> = Readonly<{
|
|
|
51
51
|
*
|
|
52
52
|
* stack.push('first');
|
|
53
53
|
*
|
|
54
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
55
54
|
* stack.push('second');
|
|
56
55
|
*
|
|
57
56
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -87,7 +86,6 @@ export type Stack<T> = Readonly<{
|
|
|
87
86
|
*
|
|
88
87
|
* stack.push('first');
|
|
89
88
|
*
|
|
90
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
91
89
|
* stack.push('second');
|
|
92
90
|
*
|
|
93
91
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -123,7 +121,6 @@ export type Stack<T> = Readonly<{
|
|
|
123
121
|
*
|
|
124
122
|
* stack.push('first');
|
|
125
123
|
*
|
|
126
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
127
124
|
* stack.push('second');
|
|
128
125
|
*
|
|
129
126
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -162,7 +159,6 @@ export type Stack<T> = Readonly<{
|
|
|
162
159
|
*
|
|
163
160
|
* stack.push('first');
|
|
164
161
|
*
|
|
165
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
166
162
|
* stack.push('second');
|
|
167
163
|
*
|
|
168
164
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -363,7 +359,6 @@ class StackClass<T> implements Stack<T> {
|
|
|
363
359
|
*
|
|
364
360
|
* stack.push('first');
|
|
365
361
|
*
|
|
366
|
-
* // eslint-disable-next-line unicorn/prefer-single-call
|
|
367
362
|
* stack.push('second');
|
|
368
363
|
*
|
|
369
364
|
* assert.isFalse(stack.isEmpty);
|
|
@@ -59,10 +59,8 @@ describe('Stack test', () => {
|
|
|
59
59
|
|
|
60
60
|
stack.push('first');
|
|
61
61
|
|
|
62
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
63
62
|
stack.push('second');
|
|
64
63
|
|
|
65
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
66
64
|
stack.push('third');
|
|
67
65
|
|
|
68
66
|
expect(stack.size).toBe(3);
|
|
@@ -130,14 +128,12 @@ describe('Stack test', () => {
|
|
|
130
128
|
|
|
131
129
|
stack.push('a');
|
|
132
130
|
|
|
133
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
134
131
|
stack.push('b');
|
|
135
132
|
|
|
136
133
|
expect(Optional.unwrap(stack.pop())).toBe('b');
|
|
137
134
|
|
|
138
135
|
stack.push('c');
|
|
139
136
|
|
|
140
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
141
137
|
stack.push('d');
|
|
142
138
|
|
|
143
139
|
expect(Optional.unwrap(stack.pop())).toBe('d');
|
|
@@ -160,7 +156,6 @@ describe('Stack test', () => {
|
|
|
160
156
|
|
|
161
157
|
stack.push(item1);
|
|
162
158
|
|
|
163
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
164
159
|
stack.push(item2);
|
|
165
160
|
|
|
166
161
|
assert.deepStrictEqual(Optional.unwrap(stack.pop()), item2);
|
|
@@ -221,13 +216,10 @@ describe('Stack test', () => {
|
|
|
221
216
|
|
|
222
217
|
stack.push('value');
|
|
223
218
|
|
|
224
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
225
219
|
stack.push(null);
|
|
226
220
|
|
|
227
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
228
221
|
stack.push(undefined);
|
|
229
222
|
|
|
230
|
-
// eslint-disable-next-line unicorn/prefer-single-call
|
|
231
223
|
stack.push('another');
|
|
232
224
|
|
|
233
225
|
expect(Optional.unwrap(stack.pop())).toBe('another');
|
package/src/object/object.mts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type RelaxedPick,
|
|
13
13
|
type StrictOmit,
|
|
14
14
|
type StrictPick,
|
|
15
|
+
type ToString,
|
|
15
16
|
type TypeEq,
|
|
16
17
|
type UnknownRecord,
|
|
17
18
|
} from 'ts-type-forge';
|
|
@@ -326,6 +327,76 @@ export namespace Obj {
|
|
|
326
327
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
327
328
|
Object.fromEntries(entries) as never;
|
|
328
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Transforms every value of a record, leaving the key set untouched.
|
|
332
|
+
*
|
|
333
|
+
* **Type Behavior**: the result is a mapped type over `keyof R`, so the keys
|
|
334
|
+
* — and their optional modifiers — survive verbatim:
|
|
335
|
+
* `{ a: number; b?: number }` maps to `{ a: V2; b?: V2 }`, and a record with
|
|
336
|
+
* an index signature stays total rather than becoming `Partial`.
|
|
337
|
+
*
|
|
338
|
+
* This is what `Object.fromEntries(Object.entries(record).map(...))` cannot
|
|
339
|
+
* express: an entries array carries no evidence that it still covers every
|
|
340
|
+
* key of the record it came from, so its element type is all the return type
|
|
341
|
+
* can be built from. Going through a mapped type instead states the
|
|
342
|
+
* invariant that a value-only transform preserves the key set.
|
|
343
|
+
*
|
|
344
|
+
* **Key handling**: keys reach the callback the way `Object.entries` yields
|
|
345
|
+
* them, so a numeric key `1` arrives as `'1'`. Symbol-keyed properties are
|
|
346
|
+
* not enumerated by `Object.entries`; they are dropped from both the result
|
|
347
|
+
* and its type.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
*
|
|
351
|
+
* <!-- doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->
|
|
352
|
+
*
|
|
353
|
+
* ```ts
|
|
354
|
+
* const scores = { alice: 1, bob: 2 } as const;
|
|
355
|
+
*
|
|
356
|
+
* const doubled = Obj.map(scores, (value) => value * 2);
|
|
357
|
+
*
|
|
358
|
+
* assert.deepStrictEqual(doubled, { alice: 2, bob: 4 });
|
|
359
|
+
*
|
|
360
|
+
* // The key is available as the second argument.
|
|
361
|
+
* const labelled = Obj.map(scores, (value, key) => `${key}=${value}`);
|
|
362
|
+
*
|
|
363
|
+
* assert.deepStrictEqual(labelled, { alice: 'alice=1', bob: 'bob=2' });
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* <!-- /doc:embed:jsdoc:example:./samples/src/object/map-example.mts -->
|
|
367
|
+
*
|
|
368
|
+
* @template R - The type of the source record
|
|
369
|
+
* @template V2 - The type the callback returns for every key
|
|
370
|
+
* @param record - The record whose values are transformed
|
|
371
|
+
* @param mapFn - Called once per own enumerable string key, with the value
|
|
372
|
+
* and the key
|
|
373
|
+
* @returns A new record with the same keys as `record` and the mapped values
|
|
374
|
+
*/
|
|
375
|
+
export const map = <const R extends UnknownRecord, V2>(
|
|
376
|
+
record: R,
|
|
377
|
+
mapFn: (
|
|
378
|
+
value: R[keyof R],
|
|
379
|
+
key: TsDataForgeInternals.MappedRecordKey<R>,
|
|
380
|
+
) => V2,
|
|
381
|
+
): TsDataForgeInternals.MappedRecord<R, V2> => {
|
|
382
|
+
// `Object.entries` erases both the key and the value type of `R`, so the
|
|
383
|
+
// callback is widened once here instead of asserting on every entry.
|
|
384
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
385
|
+
const mapFnWidened = mapFn as (value: unknown, key: string) => V2;
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
// `Object.entries` enumerates exactly the own enumerable string keys of
|
|
389
|
+
// `record`, and nothing here adds or removes an entry, so the result has
|
|
390
|
+
// the key set `MappedRecord` claims.
|
|
391
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
392
|
+
Object.fromEntries(
|
|
393
|
+
Object.entries(record).map(
|
|
394
|
+
([k, v]) => [k, mapFnWidened(v, k)] as const,
|
|
395
|
+
),
|
|
396
|
+
) as never
|
|
397
|
+
);
|
|
398
|
+
};
|
|
399
|
+
|
|
329
400
|
/**
|
|
330
401
|
* Merges multiple records into a single record using `Object.assign`.
|
|
331
402
|
* Later records override properties from earlier records with the same key.
|
|
@@ -605,6 +676,21 @@ declare namespace TsDataForgeInternals {
|
|
|
605
676
|
export type PartialIfKeyIsUnion<K, T> =
|
|
606
677
|
IsUnion<K> extends true ? Partial<T> : T;
|
|
607
678
|
|
|
679
|
+
/**
|
|
680
|
+
* The keys `Object.entries` yields for `R`: symbol keys are not enumerated,
|
|
681
|
+
* and a numeric key `1` arrives as the string `'1'`.
|
|
682
|
+
*/
|
|
683
|
+
export type MappedRecordKey<R> = ToString<RelaxedExclude<keyof R, symbol>>;
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* `R` with every value replaced by `V2`. Homomorphic over `keyof R`, so
|
|
687
|
+
* optional modifiers and index signatures are preserved; the key remapping
|
|
688
|
+
* drops the symbol keys that `Object.entries` would not have visited.
|
|
689
|
+
*/
|
|
690
|
+
export type MappedRecord<R, V2> = Readonly<{
|
|
691
|
+
[K in keyof R as K extends symbol ? never : K]: V2;
|
|
692
|
+
}>;
|
|
693
|
+
|
|
608
694
|
/**
|
|
609
695
|
* Merges two object types where keys in B override keys in A, preserving optional modifiers.
|
|
610
696
|
*
|
|
@@ -188,6 +188,103 @@ describe('fromEntries', () => {
|
|
|
188
188
|
});
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
describe('map', () => {
|
|
192
|
+
test('should map values while keeping the key set', () => {
|
|
193
|
+
const result = Obj.map({ a: 1, b: 2 }, (value) => value * 2);
|
|
194
|
+
|
|
195
|
+
expectType<typeof result, Readonly<{ a: number; b: number }>>('=');
|
|
196
|
+
|
|
197
|
+
assert.deepStrictEqual(result, { a: 2, b: 4 });
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('should keep a record with an index signature total', () => {
|
|
201
|
+
const scores: ReadonlyRecord<string, number> = {
|
|
202
|
+
alice: 1,
|
|
203
|
+
bob: 2,
|
|
204
|
+
} as const;
|
|
205
|
+
|
|
206
|
+
const result = Obj.map(scores, (value) => value > 1);
|
|
207
|
+
|
|
208
|
+
expectType<typeof result, ReadonlyRecord<string, boolean>>('=');
|
|
209
|
+
|
|
210
|
+
assert.deepStrictEqual(result, { alice: false, bob: true });
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('should preserve optional property modifiers', () => {
|
|
214
|
+
const config: Readonly<{ host: string; port?: number }> = {
|
|
215
|
+
host: 'a',
|
|
216
|
+
} as const;
|
|
217
|
+
|
|
218
|
+
const result = Obj.map(config, (value) => value === undefined);
|
|
219
|
+
|
|
220
|
+
expectType<typeof result, Readonly<{ host: boolean; port?: boolean }>>('=');
|
|
221
|
+
|
|
222
|
+
assert.deepStrictEqual(result, { host: false });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('should pass the key as the second argument', () => {
|
|
226
|
+
const result = Obj.map({ a: 1, b: 2 }, (value, key) => {
|
|
227
|
+
expectType<typeof key, 'a' | 'b'>('=');
|
|
228
|
+
|
|
229
|
+
return `${key}${value}`;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
assert.deepStrictEqual(result, { a: 'a1', b: 'b2' });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('should widen the value to the union of the record values', () => {
|
|
236
|
+
const result = Obj.map({ a: 1, b: 'x' }, (value) => {
|
|
237
|
+
expectType<typeof value, 1 | 'x'>('=');
|
|
238
|
+
|
|
239
|
+
return value;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expectType<typeof result, Readonly<{ a: 1 | 'x'; b: 1 | 'x' }>>('=');
|
|
243
|
+
|
|
244
|
+
assert.deepStrictEqual(result, { a: 1, b: 'x' });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('should stringify numeric keys, as Object.entries does', () => {
|
|
248
|
+
const result = Obj.map({ 1: 'x', 2: 'y' }, (value, key) => {
|
|
249
|
+
expectType<typeof key, '1' | '2'>('=');
|
|
250
|
+
|
|
251
|
+
return `${key}${value}`;
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expectType<typeof result, Readonly<{ 1: string; 2: string }>>('=');
|
|
255
|
+
|
|
256
|
+
assert.deepStrictEqual(result, { 1: '1x', 2: '2y' });
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('should drop symbol keys, as Object.entries does', () => {
|
|
260
|
+
const sym = Symbol('sym');
|
|
261
|
+
|
|
262
|
+
const withSymbol = { [sym]: 1, a: 2 } as const;
|
|
263
|
+
|
|
264
|
+
const result = Obj.map(withSymbol, (value) => value * 2);
|
|
265
|
+
|
|
266
|
+
expectType<typeof result, Readonly<{ a: number }>>('=');
|
|
267
|
+
|
|
268
|
+
assert.deepStrictEqual(result, { a: 4 });
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('should handle an empty record', () => {
|
|
272
|
+
const result = Obj.map({}, (value) => value);
|
|
273
|
+
|
|
274
|
+
assert.deepStrictEqual(result, {});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('should not mutate the source record', () => {
|
|
278
|
+
const source = { a: 1, b: 2 } as const;
|
|
279
|
+
|
|
280
|
+
const result = Obj.map(source, (value) => value * 2);
|
|
281
|
+
|
|
282
|
+
assert.deepStrictEqual(source, { a: 1, b: 2 });
|
|
283
|
+
|
|
284
|
+
assert.notStrictEqual<UnknownRecord>(result, source);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
191
288
|
describe('merge', () => {
|
|
192
289
|
test('should merge two objects, later overriding earlier', () => {
|
|
193
290
|
const a = { a: 0, b: 0 } as const;
|