TypeScript按键名给类属性赋值时类型报错问题求助
问题描述
我有两个对象:一个是CreateUserDto类的实例,另一个是IUser接口类型的对象。IUser包含诸多CreateUserDto不具备的属性。我希望利用IUser对象创建并填充CreateUserDto实例中两者共有的属性,为此编写了如下代码:
// user是IUser类型,createUserDto是CreateUserDto类型 Object.keys(user).filter(key => key in createUserDto).forEach(key => { // key仅包含两者共有的属性键,且对应类型一致 let test = createUserDto[key as keyof CreateUserDto]; let test2 = user[key as keyof CreateUserDto]; // 以下两行可正常运行 test = test2; test = user[key as keyof CreateUserDto]; // 以下两行无法运行... createUserDto[key as keyof CreateUserDto] = test2; createUserDto[key as keyof CreateUserDto] = user[key as keyof CreateUserDto]; });
但最后两行代码报错,错误信息如下:
createUserDto[key as keyof CreateUserDto] = test2; Type 'string | number | boolean | Permission[] | Date | UserGroup[] | undefined' is not assignable to type 'never'. Type 'undefined' is not assignable to type 'never'.ts(2322)
createUserDto[key as keyof CreateUserDto] = user[key as keyof CreateUserDto]; Type 'string | number | boolean | Permission[] | Date | UserGroup[] | undefined' is not assignable to type 'never'.ts(2322)
问题原因与解决方案
问题原因
- 类型收窄失效:
Object.keys(user)返回的是string[],即便用filter过滤出CreateUserDto的共有键,TypeScript也无法将这些键精确收窄到keyof CreateUserDto的子集。key as keyof CreateUserDto只是强制断言,但TypeScript无法确定当前key对应的具体属性类型,只能推断为所有CreateUserDto属性类型的联合。赋值时,TypeScript会检查值是否能兼容所有可能的属性类型,最终得出never类型(没有类型能同时兼容所有联合成员),因此报错。 - 变量与对象属性赋值的差异:前两行
test = test2能运行,是因为test和test2的类型都是CreateUserDto[keyof CreateUserDto](所有属性类型的联合),联合类型之间可以互相赋值;但直接给对象属性赋值时,TypeScript需要确保值的类型完全匹配当前属性的具体类型,此时类型信息已丢失,所以触发错误。
解决方案
方法1:用泛型函数精准处理属性赋值
编写泛型函数让TypeScript跟踪具体的键类型,确保赋值类型匹配:
function assignProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) { obj[key] = value; } // 遍历赋值 Object.keys(user).forEach(key => { if (key in createUserDto) { const typedKey = key as keyof CreateUserDto; assignProperty(createUserDto, typedKey, user[typedKey] as CreateUserDto[keyof CreateUserDto]); } });
方法2:使用Object.assign(简洁高效)
如果CreateUserDto的共有属性类型和user完全匹配,直接用Object.assign更简单,TypeScript能正确处理类型:
Object.assign(createUserDto, user);
注:
Object.assign会把user的所有可枚举属性赋值给createUserDto,但CreateUserDto没有的属性会被忽略(运行时不会添加到实例上),刚好符合需求。
方法3:类型断言跳过检查(适合确定类型安全的场景)
如果能确保类型完全一致,可以用any断言跳过类型检查:
Object.keys(user).filter(key => key in createUserDto).forEach(key => { const typedKey = key as keyof CreateUserDto; (createUserDto[typedKey] as any) = user[typedKey]; });
内容的提问来源于stack exchange,提问作者sirzento
相关产品推荐
相关产品推荐

