TypeScript泛型映射类型函数报错:无法将number赋值给目标类型
解决方案
你的映射类型PropertyTypeMappedToNumber定义是没问题的,核心问题在于TypeScript无法自动识别你要将指定属性的类型从T[K]改为number的操作合法性——毕竟原始对象的这些属性类型可能和number不兼容。
要避免频繁使用类型断言,你可以通过以下方式优化:
方式一:一次性断言目标类型,再赋值
先将原始对象断言为目标类型,后续赋值操作就会被TypeScript认可:
type PropertyTypeMappedToNumber<T, K extends keyof T> = { [P in keyof T]: P extends K ? number : T[P]; }; function mapperTo42<T, K extends keyof T>(obj: T, keys: K[]): PropertyTypeMappedToNumber<T, K> { // 一次性将原始对象转为目标类型,仅需一次断言 const result = { ...obj } as PropertyTypeMappedToNumber<T, K>; for (const key of keys) { // 此时result[key]的类型已被识别为number,赋值42不会报错 result[key] = 42; } return result; }
方式二:构建更新对象后合并
通过构建仅包含待修改属性的更新对象,再与原始对象合并,最后一次性断言为目标类型:
type PropertyTypeMappedToNumber<T, K extends keyof T> = { [P in keyof T]: P extends K ? number : T[P]; }; function mapperTo42<T, K extends keyof T>(obj: T, keys: K[]): PropertyTypeMappedToNumber<T, K> { // 构建更新对象,断言其类型为指定属性的number映射 const updates = Object.fromEntries(keys.map(key => [key, 42])) as { [P in K]: number }; // 合并原始对象与更新对象,断言为目标类型 return Object.assign({}, obj, updates) as PropertyTypeMappedToNumber<T, K>; }
关于无断言的可能性
如果完全不使用类型断言,只能通过添加泛型约束限制T[K]的类型,比如要求T[K]是可以赋值给number的类型(如string | boolean | number),但这会缩小函数的适用范围——你将无法把类型完全不兼容的属性(比如对象类型)转为number。而且即便添加约束,赋值时仍需类型转换,本质还是断言操作:
type PropertyTypeMappedToNumber<T, K extends keyof T> = { [P in keyof T]: P extends K ? number : T[P]; }; // 添加约束:T[K]必须是能赋值给number的类型 function mapperTo42<T, K extends keyof T>(obj: T, keys: K[]): PropertyTypeMappedToNumber<T, K> where T[K] extends number | string | boolean { const result = { ...obj }; for (const key of keys) { result[key] = 42 as unknown as T[K]; } return result as PropertyTypeMappedToNumber<T, K>; }
综上,最实用的方案还是使用一次性类型断言,既满足需求又避免冗余的断言操作。
内容的提问来源于stack exchange,提问作者frangulyan
相关产品推荐
相关产品推荐

