You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:25:37