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.
@@ -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
- ): Map<K, V> => {
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
- ): Set<T> => {
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();