ts-data-forge 6.5.0 → 6.7.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/dist/collections/imap-mapped.d.mts +5 -12
- package/dist/collections/imap-mapped.d.mts.map +1 -1
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.d.mts +5 -12
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +6 -12
- 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 +8 -22
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/object/object.d.mts +104 -29
- package/dist/object/object.d.mts.map +1 -1
- package/dist/object/object.mjs +21 -0
- package/dist/object/object.mjs.map +1 -1
- package/package.json +22 -22
- package/src/collections/imap-mapped.mts +4 -10
- package/src/collections/imap.mts +4 -10
- package/src/collections/iset-mapped.mts +5 -10
- package/src/collections/iset.mts +8 -20
- package/src/object/object.mts +182 -62
- package/src/object/object.test.mts +291 -0
- package/src/others/cast-readonly.test.mts +2 -0
- package/src/others/fast-deep-equal.test.mts +7 -5
|
@@ -181,3 +181,294 @@ describe('fromEntries', () => {
|
|
|
181
181
|
assert.deepStrictEqual(result, { name: 'Alice' });
|
|
182
182
|
});
|
|
183
183
|
});
|
|
184
|
+
|
|
185
|
+
describe('merge', () => {
|
|
186
|
+
test('should merge two objects, later overriding earlier', () => {
|
|
187
|
+
const a = { a: 0, b: 0 } as const;
|
|
188
|
+
|
|
189
|
+
const b = { b: 1, c: 0 } as const;
|
|
190
|
+
|
|
191
|
+
const result = Obj.merge(a, b);
|
|
192
|
+
|
|
193
|
+
expectType<typeof result, Readonly<{ a: 0; b: 1; c: 0 }>>('=');
|
|
194
|
+
|
|
195
|
+
assert.deepStrictEqual(result, { a: 0, b: 1, c: 0 });
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('should merge three objects', () => {
|
|
199
|
+
const a = { x: 1, y: 2 } as const;
|
|
200
|
+
|
|
201
|
+
const b = { y: 3, z: 4 } as const;
|
|
202
|
+
|
|
203
|
+
const c = { z: 5, w: 6 } as const;
|
|
204
|
+
|
|
205
|
+
const result = Obj.merge(a, b, c);
|
|
206
|
+
|
|
207
|
+
expectType<typeof result, Readonly<{ x: 1; y: 3; z: 5; w: 6 }>>('=');
|
|
208
|
+
|
|
209
|
+
assert.deepStrictEqual(result, { x: 1, y: 3, z: 5, w: 6 });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('should return empty object when called with no arguments', () => {
|
|
213
|
+
const result = Obj.merge();
|
|
214
|
+
|
|
215
|
+
expectType<typeof result, {}>('=');
|
|
216
|
+
|
|
217
|
+
assert.deepStrictEqual(result, {});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('should return the same shape for a single argument', () => {
|
|
221
|
+
const a = { a: 1, b: 2 } as const;
|
|
222
|
+
|
|
223
|
+
const result = Obj.merge(a);
|
|
224
|
+
|
|
225
|
+
expectType<typeof result, Readonly<{ a: 1; b: 2 }>>('=');
|
|
226
|
+
|
|
227
|
+
assert.deepStrictEqual(result, { a: 1, b: 2 });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('type: later key completely overrides earlier key type', () => {
|
|
231
|
+
const a = { key: 'hello' } as const;
|
|
232
|
+
|
|
233
|
+
const b = { key: 42 } as const;
|
|
234
|
+
|
|
235
|
+
const result = Obj.merge(a, b);
|
|
236
|
+
|
|
237
|
+
expectType<typeof result, Readonly<{ key: 42 }>>('=');
|
|
238
|
+
|
|
239
|
+
assert.deepStrictEqual(result, { key: 42 });
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('type: MergeAll with four objects', () => {
|
|
243
|
+
const a = { a: 1, b: 2 } as const;
|
|
244
|
+
|
|
245
|
+
const b = { b: 3, c: 4 } as const;
|
|
246
|
+
|
|
247
|
+
const c = { c: 5, d: 6 } as const;
|
|
248
|
+
|
|
249
|
+
const d = { d: 7, e: 8 } as const;
|
|
250
|
+
|
|
251
|
+
const result = Obj.merge(a, b, c, d);
|
|
252
|
+
|
|
253
|
+
expectType<typeof result, Readonly<{ a: 1; b: 3; c: 5; d: 7; e: 8 }>>('=');
|
|
254
|
+
|
|
255
|
+
assert.deepStrictEqual(result, { a: 1, b: 3, c: 5, d: 7, e: 8 });
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('type: MergeAll with same key multiple times', () => {
|
|
259
|
+
const a = { x: 'first' } as const;
|
|
260
|
+
|
|
261
|
+
const b = { x: 'second' } as const;
|
|
262
|
+
|
|
263
|
+
const c = { x: 'third' } as const;
|
|
264
|
+
|
|
265
|
+
const result = Obj.merge(a, b, c);
|
|
266
|
+
|
|
267
|
+
expectType<typeof result, Readonly<{ x: 'third' }>>('=');
|
|
268
|
+
|
|
269
|
+
assert.deepStrictEqual(result, { x: 'third' });
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('type: MergeAll with disjoint keys', () => {
|
|
273
|
+
const a = { a: 1 } as const;
|
|
274
|
+
|
|
275
|
+
const b = { b: 2 } as const;
|
|
276
|
+
|
|
277
|
+
const c = { c: 3 } as const;
|
|
278
|
+
|
|
279
|
+
const result = Obj.merge(a, b, c);
|
|
280
|
+
|
|
281
|
+
expectType<typeof result, Readonly<{ a: 1; b: 2; c: 3 }>>('=');
|
|
282
|
+
|
|
283
|
+
assert.deepStrictEqual(result, { a: 1, b: 2, c: 3 });
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test('type: MergeAll with complex nested types', () => {
|
|
287
|
+
const a = { value: { nested: 1 } } as const;
|
|
288
|
+
|
|
289
|
+
const b = { value: { nested: 2, extra: 'text' } } as const;
|
|
290
|
+
|
|
291
|
+
const result = Obj.merge(a, b);
|
|
292
|
+
|
|
293
|
+
expectType<
|
|
294
|
+
typeof result,
|
|
295
|
+
DeepReadonly<{ value: { nested: 2; extra: 'text' } }>
|
|
296
|
+
>('=');
|
|
297
|
+
|
|
298
|
+
assert.deepStrictEqual(result, { value: { nested: 2, extra: 'text' } });
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('type: MergeAll with mixed value types', () => {
|
|
302
|
+
const a = { x: 1, y: 'string' } as const;
|
|
303
|
+
|
|
304
|
+
const b = { y: 2, z: true } as const;
|
|
305
|
+
|
|
306
|
+
const c = { z: false, w: undefined } as const;
|
|
307
|
+
|
|
308
|
+
const result = Obj.merge(a, b, c);
|
|
309
|
+
|
|
310
|
+
expectType<typeof result, Readonly<{ x: 1; y: 2; z: false; w: undefined }>>(
|
|
311
|
+
'=',
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
assert.deepStrictEqual(result, { x: 1, y: 2, z: false, w: undefined });
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('type: MergeAll with mixed value types with optional properties', () => {
|
|
318
|
+
const a: Readonly<{ x: number; y?: string }> = {
|
|
319
|
+
x: 1,
|
|
320
|
+
y: 'string',
|
|
321
|
+
} as const;
|
|
322
|
+
|
|
323
|
+
const b: Readonly<{ y?: number; z?: boolean }> = { y: 2, z: true } as const;
|
|
324
|
+
|
|
325
|
+
const c: Readonly<{ z?: boolean; w?: undefined }> = {
|
|
326
|
+
z: false,
|
|
327
|
+
w: undefined,
|
|
328
|
+
} as const;
|
|
329
|
+
|
|
330
|
+
const result = Obj.merge(a, b, c);
|
|
331
|
+
|
|
332
|
+
expectType<
|
|
333
|
+
typeof result,
|
|
334
|
+
Readonly<{ x: number; y?: string | number; z?: boolean; w?: undefined }>
|
|
335
|
+
>('=');
|
|
336
|
+
|
|
337
|
+
assert.deepStrictEqual(result, { x: 1, y: 2, z: false, w: undefined });
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('type: MergeAll empty array produces empty object', () => {
|
|
341
|
+
const result = Obj.merge();
|
|
342
|
+
|
|
343
|
+
expectType<typeof result, {}>('=');
|
|
344
|
+
|
|
345
|
+
assert.deepStrictEqual(result, {});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('type: MergeAll with arrays as values', () => {
|
|
349
|
+
const a = { items: [1, 2] } as const;
|
|
350
|
+
|
|
351
|
+
const b = { items: [3, 4, 5] } as const;
|
|
352
|
+
|
|
353
|
+
const result = Obj.merge(a, b);
|
|
354
|
+
|
|
355
|
+
expectType<typeof result, Readonly<{ items: readonly [3, 4, 5] }>>('=');
|
|
356
|
+
|
|
357
|
+
assert.deepStrictEqual(result, { items: [3, 4, 5] });
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('type: MergeAll preserves literal types', () => {
|
|
361
|
+
const a = { status: 'pending', count: 0 } as const;
|
|
362
|
+
|
|
363
|
+
const b = { status: 'completed' } as const;
|
|
364
|
+
|
|
365
|
+
const result = Obj.merge(a, b);
|
|
366
|
+
|
|
367
|
+
expectType<typeof result, Readonly<{ status: 'completed'; count: 0 }>>('=');
|
|
368
|
+
|
|
369
|
+
assert.deepStrictEqual(result, { status: 'completed', count: 0 });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test('type: MergeAll with UnknownRecord[] produces UnknownRecord', () => {
|
|
373
|
+
const records: readonly UnknownRecord[] = [
|
|
374
|
+
{ a: 1, b: 2 },
|
|
375
|
+
{ b: 3, c: 4 },
|
|
376
|
+
{ c: 5, d: 6 },
|
|
377
|
+
] as const;
|
|
378
|
+
|
|
379
|
+
const result = Obj.merge(...records);
|
|
380
|
+
|
|
381
|
+
expectType<typeof result, UnknownRecord>('=');
|
|
382
|
+
|
|
383
|
+
assert.deepStrictEqual(result, { a: 1, b: 3, c: 5, d: 6 });
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test('type: MergeAll with specific typed array', () => {
|
|
387
|
+
type MyRecord = Readonly<{ x: number; y: string }>;
|
|
388
|
+
|
|
389
|
+
const records: readonly MyRecord[] = [
|
|
390
|
+
{ x: 1, y: 'a' },
|
|
391
|
+
{ x: 2, y: 'b' },
|
|
392
|
+
{ x: 3, y: 'c' },
|
|
393
|
+
] as const;
|
|
394
|
+
|
|
395
|
+
const result = Obj.merge(...records);
|
|
396
|
+
|
|
397
|
+
expectType<typeof result, MyRecord>('=');
|
|
398
|
+
|
|
399
|
+
assert.deepStrictEqual(result, { x: 3, y: 'c' });
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test('type: MergeAll with fixed length typed array', () => {
|
|
403
|
+
type MyRecord = Readonly<{ x: number; y: string }>;
|
|
404
|
+
|
|
405
|
+
const records: ArrayOfLength<3, MyRecord> = [
|
|
406
|
+
{ x: 1, y: 'a' },
|
|
407
|
+
{ x: 2, y: 'b' },
|
|
408
|
+
{ x: 3, y: 'c' },
|
|
409
|
+
] as const;
|
|
410
|
+
|
|
411
|
+
const result = Obj.merge(...records);
|
|
412
|
+
|
|
413
|
+
expectType<typeof result, MyRecord>('=');
|
|
414
|
+
|
|
415
|
+
assert.deepStrictEqual(result, { x: 3, y: 'c' });
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test('type: MergeAll with array and optional properties', () => {
|
|
419
|
+
type RecordWithOptional = Readonly<{ x: number; y?: string }>;
|
|
420
|
+
|
|
421
|
+
const records: readonly RecordWithOptional[] = [
|
|
422
|
+
{ x: 1, y: 'a' },
|
|
423
|
+
{ x: 2 },
|
|
424
|
+
] as const;
|
|
425
|
+
|
|
426
|
+
const result = Obj.merge(...records);
|
|
427
|
+
|
|
428
|
+
expectType<typeof result, RecordWithOptional>('=');
|
|
429
|
+
|
|
430
|
+
assert.deepStrictEqual(result, { x: 2, y: 'a' });
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('type: MergeAll with fixed length typed array and optional properties', () => {
|
|
434
|
+
type RecordWithOptional = Readonly<{ x: number; y?: string }>;
|
|
435
|
+
|
|
436
|
+
const records: ArrayOfLength<2, RecordWithOptional> = [
|
|
437
|
+
{ x: 1, y: 'a' },
|
|
438
|
+
{ x: 2 },
|
|
439
|
+
] as const;
|
|
440
|
+
|
|
441
|
+
const result = Obj.merge(...records);
|
|
442
|
+
|
|
443
|
+
expectType<typeof result, RecordWithOptional>('=');
|
|
444
|
+
|
|
445
|
+
assert.deepStrictEqual(result, { x: 2, y: 'a' });
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('type: MergeAll with empty dynamic array', () => {
|
|
449
|
+
const records: readonly UnknownRecord[] = [] as const;
|
|
450
|
+
|
|
451
|
+
const result = Obj.merge(...records);
|
|
452
|
+
|
|
453
|
+
expectType<typeof result, UnknownRecord>('=');
|
|
454
|
+
|
|
455
|
+
assert.deepStrictEqual(result, {});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test('type: MergeAll with union type array', () => {
|
|
459
|
+
type RecordA = Readonly<{ a: number; b: string }>;
|
|
460
|
+
|
|
461
|
+
type RecordB = Readonly<{ c: boolean; d: number }>;
|
|
462
|
+
|
|
463
|
+
const records: readonly (RecordA | RecordB)[] = [
|
|
464
|
+
{ a: 1, b: 'text' },
|
|
465
|
+
{ c: true, d: 42 },
|
|
466
|
+
] as const;
|
|
467
|
+
|
|
468
|
+
const result = Obj.merge(...records);
|
|
469
|
+
|
|
470
|
+
expectType<typeof result, RecordA | RecordB>('=');
|
|
471
|
+
|
|
472
|
+
assert.deepStrictEqual(result, { a: 1, b: 'text', c: true, d: 42 });
|
|
473
|
+
});
|
|
474
|
+
});
|
|
@@ -64,6 +64,7 @@ describe(castDeepReadonly, () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
test('should preserve runtime value for complex structures', () => {
|
|
67
|
+
// transformer-ignore-next-line convert-to-readonly
|
|
67
68
|
const complex = {
|
|
68
69
|
users: [{ id: 1, profile: { name: 'Alice' } }],
|
|
69
70
|
settings: { theme: 'dark', options: { debug: true } },
|
|
@@ -82,6 +83,7 @@ describe(castDeepReadonly, () => {
|
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
test('should work with arrays of objects', () => {
|
|
86
|
+
// transformer-ignore-next-line convert-to-readonly
|
|
85
87
|
const data = [
|
|
86
88
|
{ id: 1, meta: { active: true } },
|
|
87
89
|
{ id: 2, meta: { active: false } },
|
|
@@ -17,7 +17,7 @@ class MyMap<K, V> extends Map<K, V> {}
|
|
|
17
17
|
|
|
18
18
|
class MySet<T> extends Set<T> {}
|
|
19
19
|
|
|
20
|
-
const emptyObj = {};
|
|
20
|
+
const emptyObj = {} as const;
|
|
21
21
|
|
|
22
22
|
type TestCase = Readonly<{
|
|
23
23
|
name: string;
|
|
@@ -367,8 +367,9 @@ describe(fastDeepEqual, () => {
|
|
|
367
367
|
describe('Maps', () => {
|
|
368
368
|
const createMap = <K, V>(
|
|
369
369
|
obj: ReadonlyRecord<string, V>,
|
|
370
|
+
// transformer-ignore-next-line convert-to-readonly
|
|
370
371
|
MapClass: new () => Map<K, V> = Map,
|
|
371
|
-
):
|
|
372
|
+
): ReadonlyMap<K, V> => {
|
|
372
373
|
const mut_m = new MapClass();
|
|
373
374
|
|
|
374
375
|
for (const key in obj) {
|
|
@@ -498,8 +499,9 @@ describe(fastDeepEqual, () => {
|
|
|
498
499
|
describe('Sets', () => {
|
|
499
500
|
const createSet = <T,>(
|
|
500
501
|
arr: readonly T[],
|
|
502
|
+
// transformer-ignore-next-line convert-to-readonly
|
|
501
503
|
SetClass: new () => Set<T> = Set,
|
|
502
|
-
):
|
|
504
|
+
): ReadonlySet<T> => {
|
|
503
505
|
const mut_s = new SetClass();
|
|
504
506
|
|
|
505
507
|
for (const value of arr) {
|
|
@@ -739,7 +741,7 @@ describe(fastDeepEqual, () => {
|
|
|
739
741
|
React.createElement('h1', { key: 'h1' }, 'Title'),
|
|
740
742
|
React.createElement('h2', { key: 'h2' }, 'Subtitle'),
|
|
741
743
|
],
|
|
742
|
-
};
|
|
744
|
+
} as const;
|
|
743
745
|
|
|
744
746
|
if (child.shouldComponentUpdate(sameProps)) {
|
|
745
747
|
child.render();
|
|
@@ -755,7 +757,7 @@ describe(fastDeepEqual, () => {
|
|
|
755
757
|
React.createElement('h1', { key: 'h1' }, 'New Title'),
|
|
756
758
|
React.createElement('h2', { key: 'h2' }, 'Subtitle'),
|
|
757
759
|
],
|
|
758
|
-
};
|
|
760
|
+
} as const;
|
|
759
761
|
|
|
760
762
|
if (child.shouldComponentUpdate(differentProps)) {
|
|
761
763
|
child.render();
|