如何为Object.assign实现更优的TypeScript类型推断?
解决TypeScript中Object.assign多源参数返回any的问题
TypeScript内置的Object.assign类型定义存在局限性:当传入的源对象数量超过4个时,返回值类型会退化为any,完全丧失了类型检查的作用。我们可以通过自定义类型推导逻辑来解决这个问题。
最优解决方案:递归合并元组类型实现精确推导
通过递归合并源参数的元组类型,我们可以让Object.assign支持任意数量的源对象,同时保持返回值类型的精确性:
// 递归合并元组内所有类型为交叉类型 type MergeSources<T extends object[]> = T extends [infer FirstSource, ...infer RestSources] ? FirstSource & MergeSources<RestSources> : {}; // 重新定义assign的类型 type TypedAssign = { <Target extends object, Sources extends object[]>( target: Target, ...sources: Sources ): Target & MergeSources<Sources>; }; // 使用示例 const target = { id: 100 }; const source1 = { name: "Alice" }; const source2 = { age: 25 }; const source3 = { email: "alice@example.com" }; const source4 = { address: "Somewhere" }; const source5 = { phone: "123456789" }; const mergedObj = (Object.assign as TypedAssign)(target, source1, source2, source3, source4, source5); // mergedObj的类型为:{ id: number } & { name: string } & { age: number } & { email: string } & { address: string } & { phone: string }
全局扩展内置类型(可选)
如果希望在整个项目中直接使用增强后的Object.assign,可以在全局类型声明文件(比如global.d.ts)中扩展ObjectConstructor:
declare global { interface ObjectConstructor { assign<Target extends object, Sources extends object[]>( target: Target, ...sources: Sources ): Target & MergeSources<Sources>; } } type MergeSources<T extends object[]> = T extends [infer F, ...infer R] ? F & MergeSources<R> : {}; // 确保这个文件被视为模块 export {};
方案原理
MergeSources类型通过递归遍历源对象的元组,将所有源对象的类型逐步合并为交叉类型;- 自定义的
TypedAssign利用可变元组类型Sources extends object[]接收任意数量的源对象,最终返回目标类型与所有源类型的交叉组合,无论源对象数量多少,都能保持完整的类型信息。
这种方案彻底解决了原内置类型的局限性,让Object.assign在任何参数数量下都能提供可靠的类型检查与推导。
内容的提问来源于stack exchange,提问作者flycran
相关产品推荐
相关产品推荐

