ts-data-forge 14.0.0 → 14.1.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 +8 -7
- package/dist/array/impl/array-utils-modification.d.mts +18 -5
- package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.d.mts.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +12 -4
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +4 -5
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts +8 -3
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.mjs +1 -1
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.d.mts +1 -1
- package/dist/collections/imap.mjs +1 -1
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +2 -2
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/functional/match.d.mts.map +1 -1
- package/dist/functional/match.mjs.map +1 -1
- package/dist/number/branded-types/int.d.mts +1 -1
- package/dist/number/branded-types/int16.d.mts +1 -1
- package/dist/number/branded-types/int32.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int16.d.mts +1 -1
- package/dist/number/branded-types/non-negative-int32.d.mts +1 -1
- package/dist/number/branded-types/non-positive-finite-number.mjs +1 -1
- package/dist/number/branded-types/non-positive-finite-number.mjs.map +1 -1
- package/dist/number/branded-types/positive-int.d.mts +1 -1
- package/dist/number/branded-types/positive-int16.d.mts +1 -1
- package/dist/number/branded-types/positive-int32.d.mts +1 -1
- package/dist/number/branded-types/positive-safe-int.d.mts +1 -1
- package/dist/number/branded-types/positive-uint16.d.mts +1 -1
- package/dist/number/branded-types/positive-uint32.d.mts +1 -1
- package/dist/number/branded-types/safe-int.d.mts +1 -1
- package/dist/number/branded-types/safe-uint.d.mts +1 -1
- package/dist/number/branded-types/uint.d.mts +1 -1
- package/dist/number/branded-types/uint16.d.mts +1 -1
- package/dist/number/branded-types/uint32.d.mts +1 -1
- package/dist/number/enum/int8.d.mts +2 -2
- package/dist/number/enum/int8.d.mts.map +1 -1
- package/dist/number/enum/int8.mjs.map +1 -1
- package/dist/number/enum/uint8.d.mts +2 -2
- package/dist/number/enum/uint8.d.mts.map +1 -1
- package/dist/number/enum/uint8.mjs.map +1 -1
- package/dist/number/num-arithmetic-types.d.mts +9 -10
- package/dist/number/num-arithmetic-types.d.mts.map +1 -1
- package/dist/number/refined-number-utils.d.mts +2 -2
- package/dist/number/refined-number-utils.d.mts.map +1 -1
- package/dist/number/refined-number-utils.mjs.map +1 -1
- package/dist/object/object.d.mts +12 -12
- package/dist/object/object.d.mts.map +1 -1
- package/dist/object/object.mjs +5 -1
- package/dist/object/object.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +2 -2
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/package.json +4 -5
- package/src/array/impl/array-utils-creation.test.mts +10 -8
- package/src/array/impl/array-utils-length-constraint-preservation.test.mts +2 -2
- package/src/array/impl/array-utils-modification.mts +41 -33
- package/src/array/impl/array-utils-modification.test.mts +77 -4
- package/src/array/impl/array-utils-shape-invariants.test.mts +526 -0
- package/src/array/impl/array-utils-slice-clamped.mts +2 -1
- package/src/array/impl/array-utils-slicing.test.mts +11 -3
- package/src/array/impl/array-utils-transformation.mts +70 -20
- package/src/array/impl/array-utils-transformation.test.mts +44 -10
- package/src/array/impl/array-utils-validation.mts +54 -10
- package/src/array/impl/array-utils-validation.test.mts +153 -3
- package/src/collections/imap-mapped.mts +1 -1
- package/src/collections/imap-mapped.test.mts +2 -2
- package/src/collections/imap.mts +2 -2
- package/src/collections/iset-mapped.mts +7 -3
- package/src/collections/iset-mapped.test.mts +1 -1
- package/src/collections/iset.mts +7 -3
- package/src/collections/iset.test.mts +9 -6
- package/src/functional/match.mts +5 -4
- package/src/functional/result.test.mts +5 -2
- package/src/guard/is-primitive.test.mts +1 -1
- package/src/guard/is-record.test.mts +4 -4
- package/src/guard/is-type.test.mts +2 -2
- package/src/number/branded-types/finite-number.test.mts +2 -2
- package/src/number/branded-types/non-negative-finite-number.test.mts +2 -2
- package/src/number/branded-types/non-positive-finite-number.mts +1 -1
- package/src/number/branded-types/non-positive-finite-number.test.mts +5 -9
- package/src/number/branded-types/non-zero-finite-number.test.mts +2 -2
- package/src/number/branded-types/positive-finite-number.test.mts +2 -2
- package/src/number/enum/int8.mts +2 -1
- package/src/number/enum/uint8.mts +6 -2
- package/src/number/num-arithmetic-types.mts +129 -24
- package/src/number/num.test.mts +11 -11
- package/src/number/refined-number-utils.mts +2 -1
- package/src/object/object.mts +29 -17
- package/src/object/object.test.mts +2 -1
- package/src/others/fast-deep-equal.mts +6 -4
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 RelaxedPick, 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 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
|
|
@@ -94,7 +94,7 @@ export declare namespace Obj {
|
|
|
94
94
|
* @param keys - A readonly array of keys to include in the result
|
|
95
95
|
* @returns A new record containing only the specified keys and their values
|
|
96
96
|
*/
|
|
97
|
-
function pick<const R extends UnknownRecord, const Keys extends readonly (keyof R)[]>(record: R, keys: Keys):
|
|
97
|
+
function pick<const R extends UnknownRecord, const Keys extends readonly (keyof R)[]>(record: R, keys: Keys): StrictPick<R, ArrayElement<Keys>>;
|
|
98
98
|
function pick<const Keys extends readonly PropertyKey[]>(keys: Keys): <const R extends UnknownRecord>(record: R) => RelaxedPick<R, ArrayElement<Keys>>;
|
|
99
99
|
/**
|
|
100
100
|
* Creates a new record that excludes the specified keys from the source
|
|
@@ -141,8 +141,8 @@ export declare namespace Obj {
|
|
|
141
141
|
* @param keys - A readonly array of keys to exclude from the result
|
|
142
142
|
* @returns A new record containing all properties except the specified keys
|
|
143
143
|
*/
|
|
144
|
-
function omit<const R extends UnknownRecord, const Keys extends readonly (keyof R)[]>(record: R, keys: Keys):
|
|
145
|
-
function omit<const Keys extends readonly PropertyKey[]>(keys: Keys): <const R extends UnknownRecord>(record: R) =>
|
|
144
|
+
function omit<const R extends UnknownRecord, const Keys extends readonly (keyof R)[]>(record: R, keys: Keys): StrictOmit<R, ArrayElement<Keys>>;
|
|
145
|
+
function omit<const Keys extends readonly PropertyKey[]>(keys: Keys): <const R extends UnknownRecord>(record: R) => RelaxedOmit<R, ArrayElement<Keys>>;
|
|
146
146
|
/**
|
|
147
147
|
* Creates an object from an array of key-value pairs with precise TypeScript
|
|
148
148
|
* typing. This is a type-safe wrapper around `Object.fromEntries` that
|
|
@@ -283,7 +283,7 @@ declare namespace TsDataForgeInternals {
|
|
|
283
283
|
/** - `[['x', 1], ['y', 3]]` -> `{ x: 1, y: 3 }` */
|
|
284
284
|
type EntriesToObject<Entries extends readonly (readonly [PropertyKey, unknown])[]> = Readonly<EntriesToObjectImpl<{}, Entries>>;
|
|
285
285
|
/** @internal */
|
|
286
|
-
type EntriesToObjectImpl<R, Entries extends readonly (readonly [PropertyKey, unknown])[]> = TypeEq<Entries['length'], 0> extends true ? R : EntriesToObjectImpl<R &
|
|
286
|
+
type EntriesToObjectImpl<R, Entries extends readonly (readonly [PropertyKey, unknown])[]> = TypeEq<Entries['length'], 0> extends true ? R : EntriesToObjectImpl<R & ReadonlyRecord<Entries[0][0], Entries[0][1]>, List.Tail<Entries>>;
|
|
287
287
|
/**
|
|
288
288
|
* - `['x' | 'y' | 'z', number][]]` -> `'x' | 'y' | 'z'`
|
|
289
289
|
* - `[['a' | 'b' | 'c', number], ...['x' | 'y' | 'z', number][]]` -> `'a' |
|
|
@@ -336,19 +336,19 @@ declare namespace TsDataForgeInternals {
|
|
|
336
336
|
* ```
|
|
337
337
|
*/
|
|
338
338
|
type MergeTwo<A extends UnknownRecord, B extends UnknownRecord> = {
|
|
339
|
-
readonly [K in keyof B as {} extends
|
|
339
|
+
readonly [K in keyof B as {} extends StrictPick<B, K> ? never : K]: B[K];
|
|
340
340
|
} & {
|
|
341
|
-
readonly [K in keyof B as {} extends
|
|
341
|
+
readonly [K in keyof B as {} extends StrictPick<B, K> ? K extends keyof A ? never : K : never]?: B[K];
|
|
342
342
|
} & {
|
|
343
|
-
readonly [K in keyof B as {} extends
|
|
343
|
+
readonly [K in keyof B as {} extends StrictPick<B, K> ? K extends keyof A ? K : never : never]?: K extends keyof A ? A[K] | B[K] : never;
|
|
344
344
|
} & {
|
|
345
|
-
readonly [K in
|
|
345
|
+
readonly [K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K> ? never : K]: A[K];
|
|
346
346
|
} & {
|
|
347
|
-
readonly [K in
|
|
347
|
+
readonly [K in RelaxedExclude<keyof A, keyof B> as {} extends StrictPick<A, K> ? K : never]?: A[K];
|
|
348
348
|
} extends infer O ? Readonly<{
|
|
349
|
-
[K in keyof O as {} extends
|
|
349
|
+
[K in keyof O as {} extends StrictPick<O, K> ? never : K]: O[K];
|
|
350
350
|
} & {
|
|
351
|
-
[K in keyof O as {} extends
|
|
351
|
+
[K in keyof O as {} extends StrictPick<O, K> ? K : never]?: O[K];
|
|
352
352
|
}> : never;
|
|
353
353
|
/** Sequentially merges a tuple of object types from left to right. */
|
|
354
354
|
type MergeAll<Records extends readonly UnknownRecord[], Acc extends UnknownRecord = {}> = IsFixedLengthList<Records> extends false ? ArrayElement<Records> : Records extends readonly [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.d.mts","sourceRoot":"","sources":["../../src/object/object.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAIvB;;;;;;;GAOG;AACH,yBAAiB,GAAG,CAAC;IACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACI,MAAM,SAAS,MACjB,aAAa,KACb,aAAa,OACZ,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,KACtC,OAQF,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,SAAgB,IAAI,CAClB,KAAK,CAAC,CAAC,SAAS,aAAa,EAC7B,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EACvC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,
|
|
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
|
@@ -85,7 +85,11 @@ var Obj;
|
|
|
85
85
|
case 1: {
|
|
86
86
|
const [keys] = args;
|
|
87
87
|
return (record) => {
|
|
88
|
-
const result = omit(
|
|
88
|
+
const result = omit(
|
|
89
|
+
record,
|
|
90
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
91
|
+
keys
|
|
92
|
+
);
|
|
89
93
|
return result;
|
|
90
94
|
};
|
|
91
95
|
}
|
|
@@ -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 RelaxedPick,\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): Pick<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 | Pick<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): Omit<R, ArrayElement<Keys>>;\n\n // Curried version\n export function omit<const Keys extends readonly PropertyKey[]>(\n keys: Keys,\n ): <const R extends UnknownRecord>(record: R) => Omit<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 Omit<R, ArrayElement<Keys>> | ((record: R) => Omit<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(record, keys as readonly (keyof R2)[]) as Omit<\n R2,\n ArrayElement<Keys>\n >;\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 & Readonly<Record<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 Pick<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 Pick<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 Pick<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 Exclude<keyof A, keyof B> as {} extends Pick<A, K> ? never : 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 Exclude<keyof A, keyof B> as {} extends Pick<A, K> ? K : 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 Pick<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 Pick<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 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.d.mts","sourceRoot":"","sources":["../../src/others/fast-deep-equal.mts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,KAAM,CAAC,KAAK,CAAC,KAAG,OAyF9C,CAAC"}
|
|
@@ -20,12 +20,12 @@ const fastDeepEqual = (a, b) => {
|
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
22
|
if (isMap(a)) {
|
|
23
|
-
return isMap(b) && a.size === b.size &&
|
|
23
|
+
return isMap(b) && a.size === b.size && a.entries().every(
|
|
24
24
|
([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key))
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
if (isSet(a)) {
|
|
28
|
-
return isSet(b) && a.size === b.size &&
|
|
28
|
+
return isSet(b) && a.size === b.size && a.entries().every(([value]) => b.has(value));
|
|
29
29
|
}
|
|
30
30
|
if (isTypedArray(a)) {
|
|
31
31
|
return isTypedArray(b) && a.length === b.length && a.every((value, index) => value === b[index]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n
|
|
1
|
+
{"version":3,"file":"fast-deep-equal.mjs","sources":["../../src/others/fast-deep-equal.mts"],"sourcesContent":["import { isMap, isRegExp, isSet, isTypedArray } from '@sindresorhus/is';\nimport { hasKey, isRecord } from '../guard/index.mjs';\n\n/**\n * The fastest deep equal with ES6 Map, Set and Typed arrays support.\n * Checks equality of Date and RegExp objects by value.\n *\n * Forked from https://github.com/epoberezkin/fast-deep-equal/blob/v3.1.3/src/index.jst\n */\nexport const fastDeepEqual = <T,>(a: T, b: T): boolean => {\n if (a === b) {\n return true;\n }\n\n if (typeof a !== typeof b) {\n return false;\n }\n\n // Arrays are handled before `isRecord`, which excludes them.\n if (Array.isArray(a)) {\n return (\n Array.isArray(b) &&\n a.constructor === b.constructor &&\n a.length === b.length &&\n a.every((ai, index) => fastDeepEqual(ai, b[index]))\n );\n }\n\n if (isRecord(a)) {\n if (!isRecord(b)) {\n return false;\n }\n\n if (a.constructor !== b.constructor) {\n return false;\n }\n\n if (isMap(a)) {\n return (\n isMap(b) &&\n a.size === b.size &&\n a\n .entries()\n .every(\n ([key, value]) => b.has(key) && fastDeepEqual(value, b.get(key)),\n )\n );\n }\n\n if (isSet(a)) {\n return (\n isSet(b) &&\n a.size === b.size &&\n a.entries().every(([value]) => b.has(value))\n );\n }\n\n if (isTypedArray(a)) {\n return (\n isTypedArray(b) &&\n a.length === b.length &&\n a.every((value, index) => value === b[index])\n );\n }\n\n if (isRegExp(a)) {\n return isRegExp(b) && a.source === b.source && a.flags === b.flags;\n }\n\n if (a.valueOf !== Object.prototype.valueOf) {\n return a.valueOf() === b.valueOf();\n }\n\n if (a.toString !== Object.prototype.toString) {\n return a.toString() === b.toString();\n }\n\n const aKeys: readonly string[] = Object.keys(a);\n\n const bKeys: readonly string[] = Object.keys(b);\n\n return (\n aKeys.length === bKeys.length &&\n aKeys.every(\n (key) =>\n hasKey(b, key) &&\n // React-specific: avoid traversing React elements' _owner.\n // _owner contains circular references\n // and is not needed when comparing the actual elements (and not their owners)\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n !(key === '_owner' && a['$$typeof']) &&\n fastDeepEqual(a[key], b[key]),\n )\n );\n }\n\n // true if both NaN, false otherwise\n return Number.isNaN(a) && Number.isNaN(b);\n};\n"],"names":[],"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.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@sindresorhus/is": "^8.1.0",
|
|
39
|
-
"ts-type-forge": "
|
|
39
|
+
"ts-type-forge": "~9.1.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@emotion/react": "11.14.0",
|
|
43
43
|
"@emotion/styled": "11.14.1",
|
|
44
44
|
"@mui/material": "9.2.0",
|
|
45
|
-
"@types/react": "19.2.
|
|
45
|
+
"@types/react": "19.2.18",
|
|
46
46
|
"immer": "11.1.15",
|
|
47
|
-
"react": "19.2.
|
|
47
|
+
"react": "19.2.8",
|
|
48
48
|
"typedoc": "0.28.20",
|
|
49
49
|
"typedoc-github-theme": "0.4.0"
|
|
50
50
|
},
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"tsc": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
86
86
|
"tscw": "node ../../node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
|
|
87
87
|
"type-check": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
88
|
-
"update-packages": "pnpm update --latest",
|
|
89
88
|
"z:prettier": "prettier --config ../../.prettierrc --ignore-path ../../.prettierignore --ignore-path ../../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --write",
|
|
90
89
|
"z:vitest": "vitest --config ./configs/vitest.config.mts",
|
|
91
90
|
"z:vitest:node": "pnpm run z:vitest --project='Node.js'"
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type FixedLengthTuple,
|
|
3
|
+
type SafeInt,
|
|
4
|
+
type SafeUint,
|
|
5
|
+
type SmallUint,
|
|
6
|
+
} from 'ts-type-forge';
|
|
2
7
|
import { expectType } from '../../expect-type.mjs';
|
|
3
8
|
import { range as rangeIterator } from '../../iterator/index.mjs';
|
|
4
9
|
import {
|
|
@@ -24,7 +29,7 @@ describe('Arr creations', () => {
|
|
|
24
29
|
test('fixed length', () => {
|
|
25
30
|
const result = zeros(3);
|
|
26
31
|
|
|
27
|
-
expectType<typeof result,
|
|
32
|
+
expectType<typeof result, FixedLengthTuple<3, 0>>('=');
|
|
28
33
|
|
|
29
34
|
assert.deepStrictEqual(result, [0, 0, 0]);
|
|
30
35
|
});
|
|
@@ -125,7 +130,7 @@ describe('Arr creations', () => {
|
|
|
125
130
|
test('fixed length with primitive value', () => {
|
|
126
131
|
const result = create(3, 'a');
|
|
127
132
|
|
|
128
|
-
expectType<typeof result,
|
|
133
|
+
expectType<typeof result, FixedLengthTuple<3, 'a'>>('=');
|
|
129
134
|
|
|
130
135
|
assert.deepStrictEqual(result, ['a', 'a', 'a']);
|
|
131
136
|
});
|
|
@@ -133,7 +138,7 @@ describe('Arr creations', () => {
|
|
|
133
138
|
test('fixed length with null', () => {
|
|
134
139
|
const result = create(2, null);
|
|
135
140
|
|
|
136
|
-
expectType<typeof result,
|
|
141
|
+
expectType<typeof result, FixedLengthTuple<2, null>>('=');
|
|
137
142
|
|
|
138
143
|
assert.deepStrictEqual(result, [null, null]);
|
|
139
144
|
});
|
|
@@ -144,10 +149,7 @@ describe('Arr creations', () => {
|
|
|
144
149
|
const result = create(2, obj);
|
|
145
150
|
|
|
146
151
|
// transformer-ignore-next-line
|
|
147
|
-
expectType<
|
|
148
|
-
typeof result,
|
|
149
|
-
readonly [Readonly<{ id: 1 }>, Readonly<{ id: 1 }>]
|
|
150
|
-
>('~=');
|
|
152
|
+
expectType<typeof result, FixedLengthTuple<2, Readonly<{ id: 1 }>>>('~=');
|
|
151
153
|
|
|
152
154
|
assert.deepStrictEqual(result, [obj, obj]);
|
|
153
155
|
|
|
@@ -88,7 +88,7 @@ describe('map preserves length constraints', () => {
|
|
|
88
88
|
test('a plain tuple still maps element-wise', () => {
|
|
89
89
|
const result = map([1, 2, 3] as const, String);
|
|
90
90
|
|
|
91
|
-
expectType<typeof result,
|
|
91
|
+
expectType<typeof result, FixedLengthTuple<3, string>>('=');
|
|
92
92
|
|
|
93
93
|
assert.deepStrictEqual(result, ['1', '2', '3']);
|
|
94
94
|
});
|
|
@@ -268,7 +268,7 @@ describe('creation utilities report an exact length past their tuple range', ()
|
|
|
268
268
|
test('a length still inside the tuple range keeps its exact tuple type', () => {
|
|
269
269
|
const result = create(3, 'x');
|
|
270
270
|
|
|
271
|
-
expectType<typeof result,
|
|
271
|
+
expectType<typeof result, FixedLengthTuple<3, 'x'>>('=');
|
|
272
272
|
|
|
273
273
|
assert.deepStrictEqual(result, ['x', 'x', 'x']);
|
|
274
274
|
});
|
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
type ChangeArrayElement,
|
|
3
3
|
type ConstrainedList,
|
|
4
4
|
type FixedLengthTuple,
|
|
5
|
-
type HasLengthConstraint,
|
|
6
5
|
type Increment,
|
|
7
6
|
type IsFixedLengthList,
|
|
8
7
|
type NonEmptyArray,
|
|
9
|
-
type
|
|
8
|
+
type UnknownBrand,
|
|
10
9
|
} from 'ts-type-forge';
|
|
11
10
|
import { asPositiveUint32 } from '../../number/index.mjs';
|
|
12
11
|
import { castMutable } from '../../others/index.mjs';
|
|
@@ -356,29 +355,33 @@ const toUnshiftedImpl = <Ar extends readonly unknown[], const V>(
|
|
|
356
355
|
* assert.deepStrictEqual(filledCurried, ['x', 'x', 'x']);
|
|
357
356
|
* ```
|
|
358
357
|
*/
|
|
358
|
+
export function toFilled<
|
|
359
|
+
const Ar extends readonly unknown[] & UnknownBrand,
|
|
360
|
+
const V,
|
|
361
|
+
>(array: Ar, value: V): ChangeArrayElement<Ar, V>;
|
|
362
|
+
|
|
363
|
+
// For an array or tuple that carries no length brand the homomorphic mapping
|
|
364
|
+
// already covers every case the conditional spelled out — a fixed-length tuple
|
|
365
|
+
// keeps its length, `readonly [A, ...A[]]` stays non-empty, and `readonly A[]`
|
|
366
|
+
// stays unbounded. Stating it directly is what keeps this usable from a
|
|
367
|
+
// caller that is itself generic over the array: a conditional a *generic* `Ar`
|
|
368
|
+
// cannot decide stays deferred, and its branded branch is then not assignable
|
|
369
|
+
// to the caller's own mapping.
|
|
359
370
|
export function toFilled<const Ar extends readonly unknown[], const V>(
|
|
360
371
|
array: Ar,
|
|
361
372
|
value: V,
|
|
362
|
-
):
|
|
363
|
-
? ChangeArrayElement<Ar, V>
|
|
364
|
-
: IsFixedLengthList<Ar> extends true
|
|
365
|
-
? FixedLengthTuple<Ar['length'], V>
|
|
366
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
367
|
-
? NonEmptyArray<V>
|
|
368
|
-
: readonly V[];
|
|
373
|
+
): Readonly<{ [K in keyof Ar]: V }>;
|
|
369
374
|
|
|
370
|
-
// curried version
|
|
375
|
+
// curried version — the two cases have to be overloads of the *returned*
|
|
376
|
+
// function, spelled as an intersection of function types (see `Arr.map`).
|
|
371
377
|
export function toFilled<const V>(
|
|
372
378
|
value: V,
|
|
373
|
-
): <const Ar extends readonly unknown[]>(
|
|
379
|
+
): (<const Ar extends readonly unknown[] & UnknownBrand>(
|
|
374
380
|
array: Ar,
|
|
375
|
-
) =>
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
380
|
-
? NonEmptyArray<V>
|
|
381
|
-
: readonly V[];
|
|
381
|
+
) => ChangeArrayElement<Ar, V>) &
|
|
382
|
+
(<const Ar extends readonly unknown[]>(
|
|
383
|
+
array: Ar,
|
|
384
|
+
) => Readonly<{ [K in keyof Ar]: V }>);
|
|
382
385
|
|
|
383
386
|
export function toFilled<E>(
|
|
384
387
|
...args: readonly [array: readonly E[], value: E] | readonly [value: E]
|
|
@@ -412,6 +415,20 @@ const toFilledImpl = <E,>(array: readonly E[], value: E): readonly E[] =>
|
|
|
412
415
|
* assert.deepStrictEqual(filledPrefix, [8, 8, 2, 3, 4]);
|
|
413
416
|
* ```
|
|
414
417
|
*/
|
|
418
|
+
export function toRangeFilled<
|
|
419
|
+
const Ar extends readonly unknown[] & UnknownBrand,
|
|
420
|
+
const V,
|
|
421
|
+
>(
|
|
422
|
+
array: Ar,
|
|
423
|
+
value: V,
|
|
424
|
+
fillRange: readonly [
|
|
425
|
+
start: ArgArrayIndexWithNegative<Ar>,
|
|
426
|
+
end: ArgArrayIndexWithNegative<Ar>,
|
|
427
|
+
],
|
|
428
|
+
): ChangeArrayElement<Ar, V | Ar[number]>;
|
|
429
|
+
|
|
430
|
+
// See `toFilled` for why the unbranded case states the homomorphic mapping
|
|
431
|
+
// directly instead of going through a conditional.
|
|
415
432
|
export function toRangeFilled<const Ar extends readonly unknown[], const V>(
|
|
416
433
|
array: Ar,
|
|
417
434
|
value: V,
|
|
@@ -419,13 +436,7 @@ export function toRangeFilled<const Ar extends readonly unknown[], const V>(
|
|
|
419
436
|
start: ArgArrayIndexWithNegative<Ar>,
|
|
420
437
|
end: ArgArrayIndexWithNegative<Ar>,
|
|
421
438
|
],
|
|
422
|
-
):
|
|
423
|
-
? ChangeArrayElement<Ar, V | Ar[number]>
|
|
424
|
-
: IsFixedLengthList<Ar> extends true
|
|
425
|
-
? FixedLengthTuple<Ar['length'], V | Ar[number]>
|
|
426
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
427
|
-
? NonEmptyArray<V | Ar[number]>
|
|
428
|
-
: readonly (V | Ar[number])[];
|
|
439
|
+
): Readonly<{ [K in keyof Ar]: V | Ar[number] }>;
|
|
429
440
|
|
|
430
441
|
// curried version
|
|
431
442
|
export function toRangeFilled<const V>(
|
|
@@ -434,15 +445,12 @@ export function toRangeFilled<const V>(
|
|
|
434
445
|
start: SizeType.ArgArrWithNegative,
|
|
435
446
|
end: SizeType.ArgArrWithNegative,
|
|
436
447
|
],
|
|
437
|
-
): <const Ar extends readonly unknown[]>(
|
|
448
|
+
): (<const Ar extends readonly unknown[] & UnknownBrand>(
|
|
438
449
|
array: Ar,
|
|
439
|
-
) =>
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
444
|
-
? NonEmptyArray<V | Ar[number]>
|
|
445
|
-
: readonly (V | Ar[number])[];
|
|
450
|
+
) => ChangeArrayElement<Ar, V | Ar[number]>) &
|
|
451
|
+
(<const Ar extends readonly unknown[]>(
|
|
452
|
+
array: Ar,
|
|
453
|
+
) => Readonly<{ [K in keyof Ar]: V | Ar[number] }>);
|
|
446
454
|
|
|
447
455
|
export function toRangeFilled<E, const V>(
|
|
448
456
|
...args:
|
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
import { expectType } from '../../expect-type.mjs';
|
|
9
9
|
import { asUint32 } from '../../number/index.mjs';
|
|
10
10
|
import { type SizeType } from '../../types.mjs';
|
|
11
|
+
import {
|
|
12
|
+
asBoundedLengthArray,
|
|
13
|
+
asFixedLengthArray,
|
|
14
|
+
asMinLengthArray,
|
|
15
|
+
} from './array-utils-length-bounded-array-cast.mjs';
|
|
11
16
|
import {
|
|
12
17
|
set,
|
|
13
18
|
toFilled,
|
|
@@ -243,6 +248,45 @@ describe('Arr modifications', () => {
|
|
|
243
248
|
|
|
244
249
|
assert.deepStrictEqual(result, [0, 0, 0]);
|
|
245
250
|
});
|
|
251
|
+
|
|
252
|
+
test('should preserve the tuple shape under a generic parameter', () => {
|
|
253
|
+
// A caller that is itself generic over the tuple cannot resolve a
|
|
254
|
+
// conditional return type, so the unbranded overload has to state the
|
|
255
|
+
// homomorphic mapping directly.
|
|
256
|
+
const blank = <const T extends readonly number[]>(
|
|
257
|
+
xs: T,
|
|
258
|
+
): Readonly<{ [K in keyof T]: 0 }> => toFilled(xs, 0);
|
|
259
|
+
|
|
260
|
+
const result = blank([1, 2, 3] as const);
|
|
261
|
+
|
|
262
|
+
expectType<typeof result, FixedLengthTuple<3, 0>>('=');
|
|
263
|
+
|
|
264
|
+
assert.deepStrictEqual<FixedLengthTuple<3, 0>>(result, [0, 0, 0]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('should preserve the tuple shape under a generic parameter when curried', () => {
|
|
268
|
+
const blank = <const T extends readonly number[]>(
|
|
269
|
+
xs: T,
|
|
270
|
+
): Readonly<{ [K in keyof T]: 0 }> => toFilled(0)(xs);
|
|
271
|
+
|
|
272
|
+
const result = blank([1, 2] as const);
|
|
273
|
+
|
|
274
|
+
expectType<typeof result, FixedLengthTuple<2, 0>>('=');
|
|
275
|
+
|
|
276
|
+
assert.deepStrictEqual<FixedLengthTuple<2, 0>>(result, [0, 0]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('should keep the length brand of a branded array', () => {
|
|
280
|
+
const lower: MinLengthArray<2, number> = asMinLengthArray(2, [1, 2, 3]);
|
|
281
|
+
|
|
282
|
+
const filled = toFilled(lower, 0);
|
|
283
|
+
|
|
284
|
+
expectType<typeof filled, MinLengthArray<2, 0>>('~=');
|
|
285
|
+
|
|
286
|
+
// The brand is not part of the runtime value, so compare structurally —
|
|
287
|
+
// the type argument is what widens it, no intermediate binding needed.
|
|
288
|
+
assert.deepStrictEqual<readonly number[]>(filled, [0, 0, 0]);
|
|
289
|
+
});
|
|
246
290
|
});
|
|
247
291
|
|
|
248
292
|
describe(toRangeFilled, () => {
|
|
@@ -270,6 +314,18 @@ describe('Arr modifications', () => {
|
|
|
270
314
|
assert.deepStrictEqual(result, [1, 2, 3]);
|
|
271
315
|
});
|
|
272
316
|
|
|
317
|
+
test('should preserve the tuple shape under a generic parameter', () => {
|
|
318
|
+
const blankMiddle = <const T extends readonly number[]>(
|
|
319
|
+
xs: T,
|
|
320
|
+
): Readonly<{ [K in keyof T]: number }> => toRangeFilled(xs, 0, [1, 2]);
|
|
321
|
+
|
|
322
|
+
const result = blankMiddle([1, 2, 3] as const);
|
|
323
|
+
|
|
324
|
+
expectType<typeof result, FixedLengthTuple<3, number>>('=');
|
|
325
|
+
|
|
326
|
+
assert.deepStrictEqual<FixedLengthTuple<3, number>>(result, [1, 0, 3]);
|
|
327
|
+
});
|
|
328
|
+
|
|
273
329
|
test('should clamp range to array bounds', () => {
|
|
274
330
|
const arr = [1, 2, 3] as readonly number[];
|
|
275
331
|
|
|
@@ -356,10 +412,11 @@ describe('Arr modifications', () => {
|
|
|
356
412
|
|
|
357
413
|
/* `Arr.set` / `Arr.toUpdated` across the input shapes the positional
|
|
358
414
|
`ConstrainedList.SetAt` annotation has to cope with; the suite previously
|
|
359
|
-
covered only the plain-tuple case. Written as
|
|
360
|
-
so the assertions get real overload resolution
|
|
361
|
-
|
|
362
|
-
|
|
415
|
+
covered only the plain-tuple case. Written as a function taking each shape as
|
|
416
|
+
a parameter so the assertions get real overload resolution against the
|
|
417
|
+
declared type rather than against whatever a literal happens to infer to; the
|
|
418
|
+
test below feeds it values built with the `as*` casts. */
|
|
419
|
+
const setTypeChecks = (
|
|
363
420
|
shapeTuple: readonly [1, 2, 3],
|
|
364
421
|
shapeArray: readonly number[],
|
|
365
422
|
shapeMutableArray: readonly number[],
|
|
@@ -435,3 +492,19 @@ export const setTypeChecks = (
|
|
|
435
492
|
|
|
436
493
|
expectType<typeof _updatedUnion, readonly [number, 2, number]>('=');
|
|
437
494
|
};
|
|
495
|
+
|
|
496
|
+
test('set / toUpdated type-level checks', () => {
|
|
497
|
+
const strings = ['a', 'b', 'c'] as const;
|
|
498
|
+
|
|
499
|
+
setTypeChecks(
|
|
500
|
+
[1, 2, 3],
|
|
501
|
+
[1, 2, 3],
|
|
502
|
+
[1, 2, 3],
|
|
503
|
+
asMinLengthArray(3, strings),
|
|
504
|
+
asBoundedLengthArray(2, 5, strings),
|
|
505
|
+
asFixedLengthArray(3, strings),
|
|
506
|
+
asMinLengthArray(3, [1, 2, 3, 4, 5] as const),
|
|
507
|
+
0,
|
|
508
|
+
1,
|
|
509
|
+
);
|
|
510
|
+
});
|