为何TypeScript在函数表达式内部会丢失类型收窄?
这个问题其实戳中了TypeScript对闭包变量类型分析的一个保守性逻辑,咱们一步步理清楚:
问题根源:闭包捕获的变量类型无法被持久化收窄
你遇到的情况是:在三元表达式的分支里,明明已经通过core.contract_details === null把core.contract_details的类型收窄为非null了,但在分支里定义的函数表达式内部访问core.contract_details.contract_address时,TS却报错说它可能为null。
这是因为TypeScript的类型收窄是基于当前代码块的静态分析,但当你定义一个函数表达式(不管是箭头函数还是普通function)时,这个函数是一个闭包,它捕获的是整个外部的core变量。TS的静态分析不会去追踪这个函数的执行时机和外部变量是否会被修改——哪怕你的逻辑里这个函数会被立刻返回、core也不会被改动,TS依然会保守地认为:等到这个函数真正执行的时候,core.contract_details的类型可能已经变回原来的null | { contract_address: string }了,所以不会保留之前的收窄结果。
为什么换if/普通函数没用?
你试过把三元换成if语句、把箭头函数换成普通function声明,都没解决问题——这很正常,因为不管用哪种分支语法、哪种函数定义方式,只要闭包捕获的是整个core对象,TS的保守分析逻辑就不会改变,依然会认为外部变量的类型可能发生变化。
为什么「let-lambda」(立即执行函数)能解决问题?
你用的立即执行函数技巧,本质上是把收窄后的core.contract_details固化成了一个局部参数:
- 在三元的非null分支里,
core.contract_details已经被收窄为{ contract_address: string } - 你把它作为参数传给立即执行函数,这个参数的类型会被TS锁定为非null的版本
- 内部的函数捕获的是这个局部参数,而不是原来的
core对象——TS能确定这个局部参数的类型不会被外部修改,所以在内部函数里访问它的contract_address时,就不会再报错了
这也侧面证明了:在分支的顶层代码里,TS确实已经正确收窄了core.contract_details的类型,只是这个收窄信息无法传递到闭包内部捕获的外部变量上。
备注:内容来源于stack exchange,提问作者Ignat Insarov

