如何基于键名缩小对象属性类型?TypeScript类型收窄问题
TypeScript对象遍历函数的类型缩小问题
原始遍历函数
function iterate<O extends Record<string, unknown>>(o: O, cb: (key: keyof O, value: O[keyof O], obj: O) => void) { for (const key in o) { if (o.hasOwnProperty(key)) { cb(key, o[key], o); } } }
问题场景
使用上述函数遍历具体对象时,回调内通过键名判断无法缩小value的类型,但通过对象索引却能实现类型缩小:
iterate({ name: "Sally", age: 30, }, (key, value, obj) => { // key: "name" | "age"; value: string | number if (key === "name") { const indexedValue = obj[key]; // indexedValue: string; console.log(value.toUpperCase(), indexedValue.toUpperCase()); // value.toUpperCase() 类型报错 } else { const indexedValue = obj[key]; // indexedValue: number; console.log(value.toFixed(), indexedValue.toFixed()); // value.toFixed() 类型报错 } });
解决方案:修改泛型实现类型关联
可以通过调整iterate函数的泛型定义,让回调的key和value建立一一对应的类型关联,从而实现键名判断后的类型缩小:
function iterate<O extends Record<string, unknown>>( o: O, cb: <K extends keyof O>(key: K, value: O[K], obj: O) => void ) { for (const key in o) { if (o.hasOwnProperty(key)) { cb(key as keyof O, o[key], o); } } }
修改后,当在回调内判断key === "name"时,TypeScript会自动将value的类型缩小为string;else分支中value会被缩小为number,相关方法调用不再报错。
原问题的原因解析
原函数的类型缺陷:
原函数的回调参数中,key是keyof O联合类型,value是O[keyof O]联合类型,但两者之间没有绑定关联关系——TypeScript仅知道key是对象的某个键、value是对象的某个属性值,无法确定当前key和value属于同一个属性。索引操作的类型缩小逻辑:
当通过obj[key]取值时,TypeScript会利用索引访问的类型特性:一旦key被判断为具体的字面量类型(如"name"),它会直接关联到obj对应属性的类型,因为obj的类型是明确的O,索引操作和当前key的具体值直接绑定,因此能正确缩小类型。
内容的提问来源于stack exchange,提问作者Jimmy
相关产品推荐
相关产品推荐

