TypeScript中For-in循环在Partial<Record<...>>类型下运行异常问题
问题成因
- 你定义的
Dict类型本质是Partial<Record<K, T>>,Partial工具类型会将所有属性设为可选,因此Dict<string, string>的每一个属性的静态类型天生就是string | undefined。 - TypeScript的类型系统不会将for-in循环的运行时逻辑和静态类型推断做深度绑定,它不会默认认为你通过for-in拿到的key,一定对应对象上存在、值非空的属性:一方面for-in会遍历原型链上的可枚举属性,TS无法保证这些原型属性的值类型;另一方面TS的类型推断做不到自动识别「遍历到的key必然对应对象自有属性」这个逻辑,因此会保守保留属性的undefined类型。
parseFloat方法要求入参为string类型,你传入类型为string | undefined的value自然会触发ts2345类型错误。
解决方案
可以根据你的业务场景选择以下任意一种方案:
- 方案1:添加非空断言
如果你明确确认遍历到的key都对应obj的自有属性、值必然非空,可以直接在取值时加非空断言!,告诉编译器该值一定存在:
const value = obj[key]!;
- 方案2:添加运行时判断做类型收窄
更严谨的方式是先过滤自有属性、再判断值非空,完成类型收窄后再使用:
function numberized(obj: Dict<string, string>, keys: string[]): Dict<string, string | number> { const result: Dict<string, string | number> = {}; for (const key in obj) { // 过滤原型链属性 if (obj.hasOwnProperty(key)) { const value = obj[key]; // 收窄类型为string if (value !== undefined) { result[key] = keys.includes(key) ? parseFloat(value) : value; } } } return result; }
- 方案3:调整Dict类型定义
如果你的业务场景中传入的obj不会存在缺省属性,不需要Partial标记可选,可以直接修改Dict的定义去掉Partial:
export type Dict<K extends keyof any, T> = Record<K, T>;
修改后属性类型天然为string,不存在undefined,自然不会触发类型错误。
- 方案4:参数兜底处理
调用parseFloat时对undefined情况做兜底,兼容空值场景:
result[key] = keys.includes(key) ? parseFloat(value ?? '') : value;
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

