TypeScript中switch语句内类型属性不存在错误的解决方法
TypeScript switch中联合类型收窄失败的修复方案
问题场景
我尝试实现如下逻辑(实际场景更为复杂):
interface Base { id: string; info: object; } interface A extends Base { example: boolean; } interface B extends Base { max: number; } interface ActionDataTypes { 'for_a': A; 'for_b': B; } function tryAction<A extends keyof ActionDataTypes>(id: string, action: A, data: ActionDataTypes[A]): boolean { switch(action){ case 'for_a': //do stuff break; case 'for_b': //do stuff //this is a more complicated if statement if(data.max > 12){ //do stuff } break; } }
TypeScript 抛出错误:Property 'max' does not exist on type 'A | B'. Property 'max' does not exist on type 'A'.
我已查阅以下Stack Overflow问题但未解决:
- TypeScript switch语句中的类型安全性
- TypeScript是否不将switch的case视为类型守卫?
- TypeScript有没有办法在switch的case中转换类型?
- TypeScript:当把对象存入变量时,类型守卫在switch语句中不起作用
- 为什么TypeScript抛出“属性不存在于类型上”的错误信息?
问题原因
问题出在函数的泛型参数 A 上。当使用 A extends keyof ActionDataTypes 时,TypeScript 将 A 视为一个可包含 'for_a'、'for_b' 或二者联合的泛型类型,无法通过 switch 的 case 检查直接关联泛型 A 和具体的字符串字面量,因此无法正确收窄 data 的类型。
修复方案
方案一:移除泛型,使用联合类型参数
将函数参数改为具体的联合类型,让 TypeScript 能通过 switch 自动完成类型收窄:
interface Base { id: string; info: object; } interface A extends Base { example: boolean; } interface B extends Base { max: number; } interface ActionDataTypes { 'for_a': A; 'for_b': B; } function tryAction(id: string, action: keyof ActionDataTypes, data: ActionDataTypes[keyof ActionDataTypes]): boolean { switch(action){ case 'for_a': // data 类型已收窄为 A console.log(data.example); break; case 'for_b': // data 类型已收窄为 B if(data.max > 12){ // do stuff } break; default: // 兜底处理,确保类型穷尽检查 const _exhaustiveCheck: never = action; throw new Error(`Unknown action: ${_exhaustiveCheck}`); } return true; }
方案二:保留泛型,手动类型断言
如果必须保留泛型,可以在 case 中对 data 进行类型断言:
function tryAction<A extends keyof ActionDataTypes>(id: string, action: A, data: ActionDataTypes[A]): boolean { switch(action){ case 'for_a': // do stuff break; case 'for_b': // 手动断言为 B 类型 const bData = data as B; if(bData.max > 12){ // do stuff } break; default: const _exhaustiveCheck: never = action; throw new Error(`Unknown action: ${_exhaustiveCheck}`); } return true; }
方案三:自定义类型守卫函数
针对复杂场景,可以编写自定义类型守卫来关联 action 和 data 的类型:
// 自定义类型守卫,判断当前 action 和 data 是否对应 for_b function isForB(action: keyof ActionDataTypes, data: ActionDataTypes[keyof ActionDataTypes]): data is B { return action === 'for_b'; } function tryAction(id: string, action: keyof ActionDataTypes, data: ActionDataTypes[keyof ActionDataTypes]): boolean { if(isForB(action, data)){ // data 类型已收窄为 B if(data.max > 12){ // do stuff } } else if(action === 'for_a'){ // data 类型已收窄为 A // do stuff } else { const _exhaustiveCheck: never = action; throw new Error(`Unknown action: ${_exhaustiveCheck}`); } return true; }
内容的提问来源于stack exchange,提问作者Dr-Vortex-2
相关产品推荐
相关产品推荐

