使用带索引签名的联合类型时TypeScript报属性不存在错误
TypeScript索引签名场景下的类型收窄失效问题
场景重现
先定义基础接口:
interface A { type: 'A'; value: { a: 3; }; } interface B { type: 'B'; value: { b: 3; }; } interface C { detail: A | B; }
当直接使用C类型时,通过type属性可以正常完成类型收窄,代码无报错:
function test(arg: C) { if (arg.detail.type === 'A') { arg.detail.value.a; // 类型已收窄为A,可正常访问a属性 } else if (arg.detail.type === 'B') { arg.detail.value.b; // 类型已收窄为B,可正常访问b属性 } }
异常场景
当创建带索引签名的接口cArr后:
interface cArr { [index: string]: C; }
尝试通过索引访问并收窄类型时,TypeScript抛出错误:
function test(arg2: cArr, ID: string) { if (arg2[ID].detail.type === 'A') { arg2[ID].detail.value.a; // 此处报错 } }
报错信息:
Property 'a' does not exist on type '{ a: 3; } | { b: 3; }'. Property 'a' does not exist on type '{ b: 3; }'.
问题根源
- 索引访问的不确定性:TypeScript无法确保两次
arg2[ID]访问返回的是同一个值。因为带索引签名的对象允许动态修改属性,理论上在if判断和后续属性访问之间,arg2[ID]的值可能被外部代码修改,导致类型发生变化。 - 类型收窄的作用域限制:对于直接的变量访问(比如第一个例子里的
arg.detail),TypeScript能跟踪变量的类型变化;但动态索引访问的属性会被视为独立表达式,TypeScript无法将if中的类型判断结果关联到后续的索引访问操作上。
解决方法
将索引访问的结果存入临时变量,让TypeScript能跟踪这个变量的类型变化:
function test(arg2: cArr, ID: string) { const currentItem = arg2[ID]; if (currentItem.detail.type === 'A') { currentItem.detail.value.a; // 类型正常收窄,无报错 } }
通过临时变量currentItem,TypeScript可以在if判断后正确收窄其类型,从而允许访问对应的属性。
内容的提问来源于stack exchange,提问作者milad
相关产品推荐
相关产品推荐

