function deepMerge<T extends object, U extends object>(target: T, source: U): T & U {
const result = {...target } as T & U;
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === 'object' && source[key]!== null && typeof result[key] === 'object' && result[key]!== null) {
result[key] = deepMerge(result[key], source[key]);
} else {
result[key] = source[key] as T[keyof T] & U[keyof U];
}
}
}
return result;
}
// 类型推断机制分析
// 1. 函数定义中的泛型T和U分别代表两个对象的类型。
// - 函数参数target和source的类型分别为T和U,这明确了输入对象的类型范围。
// - 返回值类型为T & U,表明返回的是一个包含T和U所有属性的新对象类型。
// 2. 在函数体内部:
// - 初始化result为{...target } as T & U,这里通过展开操作符复制target对象,并断言其类型为T & U,这使得result对象从一开始就具有合并后的类型基础。
// - 遍历source对象时,对于每个属性:
// - 当source[key]和result[key]都是非空对象时,递归调用deepMerge进行深度合并。此时,类型推断会根据传入的两个对象的具体类型进一步细化内部对象的合并类型。例如,如果target.a是{a: {b: number}},source.a是{a: {c: string}},递归调用时会将{a: {b: number, c: string}}作为新的合并结果类型。
// - 当source[key]和result[key]至少有一个不是对象时,直接将source[key]赋值给result[key],这里类型断言为T[keyof T] & U[keyof U],这是因为要确保赋值的属性类型既符合T中该属性的类型,也符合U中该属性的类型。
// 类型拓宽原理分析
// 1. 在联合类型的情况下:
// - 假设T中的某个属性类型是string | number,U中的相同属性类型是number | boolean。
// - 当进行合并时,类型拓宽会使合并后的属性类型变为string | number | boolean。这是因为要保证合并后的类型能够兼容两个输入对象中该属性可能出现的所有类型。例如,如果有deepMerge({a: 1}, {a: true}),最终合并结果中a的类型就是number | boolean,体现了类型拓宽对联合类型的影响,使得类型更加包容以适应不同输入的可能性。