TypeScript reducer switch case类型守卫解构失效问题
问题现象
我编写了一个根据action.type执行不同逻辑的reducer,不同action对应的payload类型存在差异,实现代码如下:
export enum ActionType { UpdateEntireState = "UPDATE_ENTIRE_STATE", UpdateStateItem = "UPDATE_STATE_ITEM" } type TypeEditData = { id: string; name: string; surname: string; age: number; }; export type State = TypeEditData[]; export type Action = UpdateEntireState | UpdateStateItem; type UpdateEntireState = { type: ActionType.UpdateEntireState; payload: State; }; type UpdateStateItem = { type: ActionType.UpdateStateItem; payload: { id: string; data: TypeEditData }; }; export function reducer(state: State, action: Action): State { const { type, payload } = action; switch (type) { case ActionType.UpdateEntireState: { return [...payload]; } case ActionType.UpdateStateItem: { const person = state.filter((item) => item.id === payload.id); return [...state, person[0]]; } default: { throw Error("Wrong type of action!"); } } }
上述代码无法通过TypeScript 4.3.4版本的类型校验,报错提示action的payload类型可能为State或{ id: string; data: TypeEditData }。但如果在switch case分支内通过点表示法直接访问payload属性,例如编写return [...action.payload];,则不会触发类型错误,判别式联合类型的类型守卫可正常工作。
类型层面的核心差异
两种写法的本质区别在于类型守卫的关联作用域是否生效:
- 当你直接通过
action.type做分支判断时,TypeScript会将类型收窄的效果绑定到原始对象action上:在case ActionType.UpdateEntireState分支内,TS会自动将整个action的类型收窄为UpdateEntireState,此时访问action.payload自然能拿到对应正确的State类型。 - 当你提前执行
const { type, payload } = action解构时,相当于创建了两个独立的变量type和payload,这两个变量的类型在解构那一刻就被确定了:type的类型是ActionType.UpdateEntireState | ActionType.UpdateStateItempayload的类型是State | { id: string; data: TypeEditData }
后续你对type做switch判断收窄类型时,TS 4.3.4版本并不会跟踪到「这个type变量和旁边的payload变量是从同一个联合类型对象里解构出来的关联属性」,自然不会联动收窄payload的类型,就会报类型不匹配的错误。
补充:这个行为是TypeScript的设计限制,并非bug。后续高版本TS(4.6及之后)对解构后的判别式收窄做了部分支持,但覆盖场景依然有限,最稳妥的写法始终是在分支内通过原始对象访问属性,或者在分支内单独对解构出来的
payload做类型断言/类型守卫。
内容的提问来源于stack exchange,提问作者Slava.In
相关产品推荐
相关产品推荐

