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 4 import { deepMerge } from '@/utils';
5 5  
6 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 31 test('should merge two objects recursively', () => {
8 32 const source = {
9 33 a: { b: { c: 1 }, d: [1, 2] },
... ... @@ -15,6 +39,7 @@ describe('deepMerge function', () => {
15 39 too: [1, 2, 3],
16 40 },
17 41 ],
  42 + r: { a: 1 },
18 43 };
19 44 const target = {
20 45 a: { b: { d: [3] } },
... ... @@ -30,6 +55,7 @@ describe('deepMerge function', () => {
30 55 really: 'yes',
31 56 },
32 57 ],
  58 + r: { a: 2 },
33 59 };
34 60 const expected = {
35 61 a: { b: { c: 1, d: [3] }, d: [1, 2] },
... ... @@ -48,8 +74,9 @@ describe('deepMerge function', () => {
48 74 },
49 75 ],
50 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 82 test('should replace arrays by default', () => {
... ... @@ -65,7 +92,7 @@ describe('deepMerge function', () => {
65 92 a: { b: { d: [3] } },
66 93 e: [3],
67 94 };
68   - expect(deepMerge(source, target)).toEqual(expected);
  95 + expect(deepMerge(source, target)).toStrictEqual(expected);
69 96 });
70 97  
71 98 test("should union arrays using mergeArrays = 'union'", () => {
... ... @@ -75,13 +102,13 @@ describe('deepMerge function', () => {
75 102 };
76 103 const target = {
77 104 a: { b: { d: [2, 3] } },
78   - e: [3],
  105 + e: [1, 3],
79 106 };
80 107 const expected = {
81 108 a: { b: { d: [1, 2, 3] } },
82 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 114 test("should intersect arrays using mergeArrays = 'intersection'", () => {
... ... @@ -97,7 +124,7 @@ describe('deepMerge function', () => {
97 124 a: { b: { d: [2] } },
98 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 130 test("should concatenate arrays using mergeArrays = 'concat'", () => {
... ... @@ -113,6 +140,6 @@ describe('deepMerge function', () => {
113 140 a: { b: { d: [1, 2, 2, 3] } },
114 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 58 if (!source) {
59 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 83 export function openWindow(
... ...