TypeScript无法推断对象值:为何两种函数写法报错情况不同?
TypeScript可选对象属性守卫失效问题解析
我们在TypeScript里定义了枚举PropTypes作为值列表,并用它创建了一个可选键对象mapObj。在test函数里,先判断参数str是否存在,再判断mapObj是否包含str对应的键,但调用mapObj[str]()时,TypeScript还是抛出错误:Cannot invoke an object which is possibly 'undefined',明明已经做了守卫判断。
问题代码示例
enum PropTypes { typeOne = 'valueOne', typeTwo = 'valueTwo', typeThree = 'valueThree', } const mapObj: { [k in PropTypes]?: () => string } = { [PropTypes.typeTwo]: () => 'bere', } function test(str?: PropTypes) { if (!str) { return; } if (!mapObj[str]) { return; } return mapObj[str](); }
修复后的代码示例
如果把test函数改成下面这样,错误就消失了:
function test(str?: PropTypes) { if (!str) { return; } const fn = mapObj[str] if (!fn) { return true } return fn(); }
原因解析
这和引用无关,核心是TypeScript的类型守卫作用范围限制:
- 第一种写法中,
if (!mapObj[str])只能确保当前语句中mapObj[str]不是undefined,但TypeScript无法保证后续访问mapObj[str]时,这个属性的值没有被外部修改(哪怕mapObj是const,它的属性依然可以被重新赋值),所以第二次访问mapObj[str]时,TypeScript依然认为它可能是undefined。 - 第二种写法里,我们把
mapObj[str]赋值给了局部常量fn,TypeScript可以确定这个常量在后续代码中不会被修改。通过if (!fn)守卫后,TypeScript能明确fn一定是一个函数,因此调用fn()时不会触发类型错误。
简单来说:直接访问对象属性时,TypeScript无法跨语句跟踪属性的确定性;但将属性值存入局部常量后,常量的类型可以被准确守卫,后续使用就不会有问题。
内容的提问来源于stack exchange,提问作者Comum
相关产品推荐
相关产品推荐

