如何让TypeScript将字符串用作联合类型的键以消除索引错误?
TypeScript联合类型索引报错的解决方法
问题场景
原代码如下:
type Point = { x: number, y: number }; type Point3D = Point & { z: number } function printPoint(p: Point | Point3D) { log("Point {"); for (const coordinate in p) { log("\t", p[coordinate]) } log("}") } printPoint({ x: 1, y: 2, z: 3 }) printPoint({ x: 0.4, y: 4 })
这段代码触发错误:Element implicitly has an 'any' type because expression of type 'string' can't be used to index type ..
尝试了以下写法,仅第一种有效,其余均无效:
// 有效 for (const coordinate in p) { log("\t", p[coordinate as keyof Point]) } // 无效 for (const coordinate in p) { log("\t", p[coordinate as keyof Point3D]) } // 也无效 for (const coordinate in p) { log("\t", p[coordinate as keyof Point | keyof Point3D]) }
请问如何正确处理,让字符串索引能适用于联合/交叉类型?
解决方法
方法1:使用keyof typeof p断言
直接将循环变量断言为当前参数p的键类型,TypeScript会根据传入的p的实际类型推断出对应的键:
function printPoint(p: Point | Point3D) { log("Point {"); for (const coordinate in p) { log("\t", p[coordinate as keyof typeof p]) } log("}") }
方法2:泛型约束函数参数
通过泛型让TypeScript明确参数p的具体类型,避免联合类型带来的模糊性:
function printPoint<T extends Point | Point3D>(p: T) { log("Point {"); for (const coordinate in p) { log("\t", p[coordinate as keyof T]) } log("}") }
方法3:断言为通用记录类型(类型安全性稍弱)
如果确定所有属性值都是number类型,可以将p断言为Record<string, number>,这种写法更简洁但会丢失部分类型检查:
function printPoint(p: Point | Point3D) { log("Point {"); const numPoint = p as Record<string, number>; for (const coordinate in numPoint) { log("\t", numPoint[coordinate]) } log("}") }
为什么之前的写法无效?
- 断言为
keyof Point有效,是因为x、y是Point和Point3D共有的属性,无论p是哪种类型,这两个键都必然存在。 - 断言为
keyof Point3D无效,是当p为Point类型时,它不存在z属性,TypeScript会拦截这种可能的非法属性访问。 keyof Point | keyof Point3D等价于keyof Point3D(因为keyof Point是x|y,和x|y|z联合后还是x|y|z),同样存在访问Point不存在的z属性的风险,因此报错。
内容的提问来源于stack exchange,提问作者dodo
相关产品推荐
相关产品推荐

