TypeScript泛型拷贝函数类型报错:不用'as'如何解决?
问题描述
我找到了一些类似问题,但不知道怎么用更符合TypeScript风格的方式解决,不想用as类型断言。以下是我的问题示例:
错误一:Type '{}' is not assignable to type 'T'
function copy<T extends Record<string, any>>(target: T): T { const res: T = {} // Error 1: Type '{}' is not assignable to type 'T'. Object.keys(target).forEach(key => { if (typeof target[key] === 'object') res[key] = copy(target[key]) else if (typeof target[key] === 'string') res[key] = target[key]; }); return res }
错误二:Type 'Record<string, any>' is not assignable to type 'T'
function copy<T extends Record<string, any>>(target: T): T { const res: Record<string, any> = {}; Object.keys(target).forEach(key => { if (typeof target[key] === 'object') res[key] = copy(target[key]) else if (typeof target[key] === 'string') res[key] = target[key]; }); return res // Type 'Record<string, any>' is not assignable to type 'T'. }
我目前用as断言解决了问题,代码如下:
function copy<T extends Record<string, any>>(target: T): T { const res: Record<string, any> = {}; Object.keys(target).forEach(key => { if (typeof target[key] === 'object') res[key] = copy(target[key]) else if (typeof target[key] === 'string') res[key] = target[key]; }); return res as T; }
请问是否有其他解决方法?或者如何在T继承object或Record<string, any>时,实现传入T类型并返回T类型的函数?
解决方案
方法一:利用目标对象的构造函数创建实例
既然T是对象类型,通过target.constructor创建同类型的空实例,让TypeScript自动匹配类型:
function copy<T extends Record<string, any>>(target: T): T { // 创建与target同类型的空实例 const res = new target.constructor() as T; Object.keys(target).forEach(key => { const value = target[key]; // 排除null,因为typeof null会返回'object' if (typeof value === 'object' && value !== null) { res[key] = copy(value); } else if (typeof value === 'string') { res[key] = value; } }); return res; }
方法二:用Object.assign初始化结果
通过Object.assign将空对象合并为T类型变量,利用TypeScript的类型兼容性解决赋值问题:
function copy<T extends Record<string, any>>(target: T): T { // 先浅拷贝target的属性,初始化符合T类型的变量 let res: T = Object.assign({}, target); Object.keys(res).forEach(key => { const value = res[key]; if (typeof value === 'object' && value !== null) { res[key] = copy(value); } }); return res; }
方法三:定义递归深拷贝类型(更精确的类型约束)
通过自定义递归类型DeepCopy<T>,让类型推导更严谨,仅在必要场景使用类型断言:
// 定义递归的深拷贝类型 type DeepCopy<T> = T extends string ? T : T extends Record<string, any> ? { [K in keyof T]: DeepCopy<T[K]> } : T; function copy<T extends string | Record<string, any>>(target: T): DeepCopy<T> { if (typeof target === 'string') { return target as DeepCopy<T>; } const res: Record<string, any> = {}; Object.keys(target).forEach(key => { const value = target[key]; if (typeof value === 'object' && value !== null) { res[key] = copy(value); } else { res[key] = value; } }); return res as DeepCopy<T>; }
内容的提问来源于stack exchange,提问作者JianCheng.Kang
相关产品推荐
相关产品推荐

