为何TypeScript闭包内类型收窄对对象属性无效却对变量有效?
TypeScript内部函数类型收窄的对象属性问题解析
问题现象
先看第一个会报错的代码片段:
type MyType = { optional?: string } (arr: string[], param: MyType) => { if (param.optional) { const includes = arr.includes(param.optional) // 这里完全正常 arr.filter(function (item) { return item.includes(param.optional) // 报错:类型undefined不可赋值给string }) } }
在if (param.optional)的分支里,外层直接使用param.optional没问题,但放到filter的回调函数里,TypeScript就会警告该属性可能是undefined。
但如果把param.optional先赋值给一个独立变量,就能正常完成类型收窄:
type MyType = { optional?: string } (arr: string[], param: MyType) => { const variable = param.optional if (variable) { const includes = arr.includes(variable) // 正常 arr.filter(function (item) { return item.includes(variable) // 完全正常 }) } }
根本原因
这本质是TypeScript对引用类型的属性追踪限制:
- 对象是引用类型,
param是一个对象引用。虽然你在if分支里确认了param.optional存在,但filter的回调函数属于延迟执行逻辑(TypeScript的类型检查不会区分实际是否同步执行),在回调运行前,外部代码有可能修改param.optional,将其改为undefined。TypeScript无法保证回调执行时param.optional的类型和分支检查时一致,所以不会把类型收窄的结果延伸到内部函数中。 - 当你把
param.optional赋值给const variable时,因为variable是原始类型的常量,一旦赋值就无法被修改(原始类型是值传递,内部函数捕获的是值的副本)。TypeScript能确定回调执行时variable依然是string类型,所以能正确保留类型收窄的结果。
如果参数本身是string | undefined这种原始类型,就不会出现这个问题——原始类型的值被内部函数捕获后,外部无法修改捕获的副本,TypeScript能安全地将类型收窄的结果延伸到内部函数中。
内容的提问来源于stack exchange,提问作者rmoret
相关产品推荐
相关产品推荐

