TypeScript中带公共属性的对象判别联合类型收窄后,直接访问属性为何无法通过泛型函数类型校验?
这是个很典型的TypeScript类型收窄细节问题,我来给你拆解背后的原因:
核心原因:TypeScript对嵌套属性的类型跟踪局限性
首先,你通过action.payload.category !== Category.ROUTE_POINT的判断,确实已经把action.payload的类型收窄到了联合类型Payload中对应的分支——也就是{ category: Category.ROUTE_POINT; id: string; newValues: Partial<RoutePoint>; },这也是你悬浮查看action.payload.newValues时能看到正确类型的原因。
但问题出在回调函数的上下文里:当你在ArrayUtils.updateItemInArray的第三个参数(那个(p) => { ... }回调)中直接访问action.payload.newValues时,TypeScript的静态类型检查器没办法保证action.payload在回调执行时还是之前收窄后的类型。虽然在你的业务逻辑里action.payload肯定不会被修改,但TypeScript会做最保守的假设——它认为外部对象的属性可能在回调执行前被篡改,所以会把action.payload.newValues的类型退回到联合类型的原始状态:Partial<RoutePoint> | Partial<UnassignedRoutePoint>,这就和ArrayUtils.updateObject要求的Partial<RoutePoint>类型不匹配,导致报错。
为什么赋值给变量就生效?
当你把action.payload.newValues赋值给const newValues这个局部常量时,情况就不一样了:TypeScript会把这个常量的类型直接锁定为收窄后的Partial<RoutePoint>。因为newValues是局部的、不可修改的常量,TypeScript能确定它的类型在回调执行时不会发生变化,所以在回调里使用它时,类型校验自然就能通过。
额外小提示
如果你尝试把newValues改成let声明,可能会再次出现类型问题——因为let变量存在被修改的可能,TypeScript不会帮你锁定它的类型。所以这种场景下,用const声明局部变量是最稳妥的做法。
内容的提问来源于stack exchange,提问作者RouteMapper

