Commit 7535db377f995c4c82944c74c7e1cd3d51eb6c55

Authored by Kirk Lin
Committed by GitHub
1 parent a0fdceea

fix(utils): deepMerge failing to correctly merge basic data types (#2872)

src/utils/__test__/index.test.ts
@@ -4,6 +4,30 @@ import { describe, expect, test } from 'vitest'; @@ -4,6 +4,30 @@ import { describe, expect, test } from 'vitest';
4 import { deepMerge } from '@/utils'; 4 import { deepMerge } from '@/utils';
5 5
6 describe('deepMerge function', () => { 6 describe('deepMerge function', () => {
  7 + test('should correctly merge basic data types', () => {
  8 + const source = { a: 1, b: 2, c: null };
  9 + const target = {
  10 + a: 2,
  11 + b: undefined,
  12 + c: 3,
  13 + };
  14 + const expected = {
  15 + a: 2,
  16 + b: 2,
  17 + c: 3,
  18 + };
  19 + expect(deepMerge(source, target)).toStrictEqual(expected);
  20 + });
  21 +
  22 + test('should return the same date if only 1 is passed', () => {
  23 + const foo = new Date();
  24 + const merged = deepMerge(foo, null);
  25 + const merged2 = deepMerge(undefined, foo);
  26 + expect(merged).toStrictEqual(foo);
  27 + expect(merged2).toStrictEqual(foo);
  28 + expect(merged).toStrictEqual(merged2);
  29 + });
  30 +
7 test('should merge two objects recursively', () => { 31 test('should merge two objects recursively', () => {
8 const source = { 32 const source = {
9 a: { b: { c: 1 }, d: [1, 2] }, 33 a: { b: { c: 1 }, d: [1, 2] },
@@ -15,6 +39,7 @@ describe('deepMerge function', () => { @@ -15,6 +39,7 @@ describe('deepMerge function', () => {
15 too: [1, 2, 3], 39 too: [1, 2, 3],
16 }, 40 },
17 ], 41 ],
  42 + r: { a: 1 },
18 }; 43 };
19 const target = { 44 const target = {
20 a: { b: { d: [3] } }, 45 a: { b: { d: [3] } },
@@ -30,6 +55,7 @@ describe('deepMerge function', () => { @@ -30,6 +55,7 @@ describe('deepMerge function', () => {
30 really: 'yes', 55 really: 'yes',
31 }, 56 },
32 ], 57 ],
  58 + r: { a: 2 },
33 }; 59 };
34 const expected = { 60 const expected = {
35 a: { b: { c: 1, d: [3] }, d: [1, 2] }, 61 a: { b: { c: 1, d: [3] }, d: [1, 2] },
@@ -48,8 +74,9 @@ describe('deepMerge function', () => { @@ -48,8 +74,9 @@ describe('deepMerge function', () => {
48 }, 74 },
49 ], 75 ],
50 qu: 5, 76 qu: 5,
  77 + r: { a: 2 },
51 }; 78 };
52 - expect(deepMerge(source, target)).toEqual(expected); 79 + expect(deepMerge(source, target)).toStrictEqual(expected);
53 }); 80 });
54 81
55 test('should replace arrays by default', () => { 82 test('should replace arrays by default', () => {
@@ -65,7 +92,7 @@ describe('deepMerge function', () => { @@ -65,7 +92,7 @@ describe('deepMerge function', () => {
65 a: { b: { d: [3] } }, 92 a: { b: { d: [3] } },
66 e: [3], 93 e: [3],
67 }; 94 };
68 - expect(deepMerge(source, target)).toEqual(expected); 95 + expect(deepMerge(source, target)).toStrictEqual(expected);
69 }); 96 });
70 97
71 test("should union arrays using mergeArrays = 'union'", () => { 98 test("should union arrays using mergeArrays = 'union'", () => {
@@ -75,13 +102,13 @@ describe('deepMerge function', () => { @@ -75,13 +102,13 @@ describe('deepMerge function', () => {
75 }; 102 };
76 const target = { 103 const target = {
77 a: { b: { d: [2, 3] } }, 104 a: { b: { d: [2, 3] } },
78 - e: [3], 105 + e: [1, 3],
79 }; 106 };
80 const expected = { 107 const expected = {
81 a: { b: { d: [1, 2, 3] } }, 108 a: { b: { d: [1, 2, 3] } },
82 e: [1, 2, 3], 109 e: [1, 2, 3],
83 }; 110 };
84 - expect(deepMerge(source, target, 'union')).toEqual(expected); 111 + expect(deepMerge(source, target, 'union')).toStrictEqual(expected);
85 }); 112 });
86 113
87 test("should intersect arrays using mergeArrays = 'intersection'", () => { 114 test("should intersect arrays using mergeArrays = 'intersection'", () => {
@@ -97,7 +124,7 @@ describe('deepMerge function', () => { @@ -97,7 +124,7 @@ describe('deepMerge function', () => {
97 a: { b: { d: [2] } }, 124 a: { b: { d: [2] } },
98 e: [], 125 e: [],
99 }; 126 };
100 - expect(deepMerge(source, target, 'intersection')).toEqual(expected); 127 + expect(deepMerge(source, target, 'intersection')).toStrictEqual(expected);
101 }); 128 });
102 129
103 test("should concatenate arrays using mergeArrays = 'concat'", () => { 130 test("should concatenate arrays using mergeArrays = 'concat'", () => {
@@ -113,6 +140,6 @@ describe('deepMerge function', () => { @@ -113,6 +140,6 @@ describe('deepMerge function', () => {
113 a: { b: { d: [1, 2, 2, 3] } }, 140 a: { b: { d: [1, 2, 2, 3] } },
114 e: [1, 2, 3], 141 e: [1, 2, 3],
115 }; 142 };
116 - expect(deepMerge(source, target, 'concat')).toEqual(expected); 143 + expect(deepMerge(source, target, 'concat')).toStrictEqual(expected);
117 }); 144 });
118 }); 145 });
src/utils/index.ts
@@ -58,27 +58,26 @@ export function deepMerge<T extends object | null | undefined, U extends object @@ -58,27 +58,26 @@ export function deepMerge<T extends object | null | undefined, U extends object
58 if (!source) { 58 if (!source) {
59 return target as T & U; 59 return target as T & U;
60 } 60 }
61 - if (isArray(target) && isArray(source)) {  
62 - switch (mergeArrays) {  
63 - case 'union':  
64 - return unionWith(target, source, isEqual) as T & U;  
65 - case 'intersection':  
66 - return intersectionWith(target, source, isEqual) as T & U;  
67 - case 'concat':  
68 - return target.concat(source) as T & U;  
69 - case 'replace':  
70 - return source as T & U;  
71 - default:  
72 - throw new Error(`Unknown merge array strategy: ${mergeArrays}`);  
73 - 61 + return mergeWith({}, source, target, (sourceValue, targetValue) => {
  62 + if (isArray(targetValue) && isArray(sourceValue)) {
  63 + switch (mergeArrays) {
  64 + case 'union':
  65 + return unionWith(sourceValue, targetValue, isEqual);
  66 + case 'intersection':
  67 + return intersectionWith(sourceValue, targetValue, isEqual);
  68 + case 'concat':
  69 + return sourceValue.concat(targetValue);
  70 + case 'replace':
  71 + return targetValue;
  72 + default:
  73 + throw new Error(`Unknown merge array strategy: ${mergeArrays as string}`);
  74 + }
74 } 75 }
75 - }  
76 - if (isObject(target) && isObject(source)) {  
77 - return mergeWith({}, target, source, (targetValue, sourceValue) => {  
78 - return deepMerge(targetValue, sourceValue, mergeArrays);  
79 - }) as T & U;  
80 - }  
81 - return source as T & U; 76 + if (isObject(targetValue) && isObject(sourceValue)) {
  77 + return deepMerge(sourceValue, targetValue, mergeArrays);
  78 + }
  79 + return undefined;
  80 + });
82 } 81 }
83 82
84 export function openWindow( 83 export function openWindow(