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

如何在TypeScript的switch语句中用小写判别键推断对象联合类型?

TypeScript联合类型判别式转小写后无法正确推断类型

我希望基于name属性的值推断Option联合类型中的具体类型,但由于判别键的大小写敏感特性,遇到了TypeScript错误。尝试了两种解决方法均未生效,TypeScript仍会抛出错误。

示例代码

const toLowerCase = <T extends string>(x: T) => x.toLowerCase() as Lowercase<T>;

type Option = {
    name: "a";
    foo: "foo"
} | {
    name: "b";
    bar: "bar"
}

function printOption(option: Option) {
    switch (option.name) {
        case "a":
            console.log(option.foo)
            break
        case "b":
            console.log(option.bar)
            break
    }
}

function printOptionToLowerCase(option: Option) {
    switch (toLowerCase(option.name)) {
        case "a":
            // Property 'foo' does not exist on type 'Option'.
            // Property 'foo' does not exist on type '{ name: "b"; bar: "bar"; }'.(2339)
            console.log(option.foo)
            break
        case "b":
            // Property 'bar' does not exist on type 'Option'.
            // Property 'bar' does not exist on type '{ name: "a"; foo: "foo"; }'.(2339)
            console.log(option.bar)
            break
    }
}

function printOptionAsserObjectField(option: Option) {
    switch (option.name.toLowerCase() as Option["name"]) {
        case "a":
            // Property 'foo' does not exist on type 'Option'.
            // Property 'foo' does not exist on type '{ name: "b"; bar: "bar"; }'.(2339)
            console.log(option.foo)
            break
        case "b":
            // Property 'bar' does not exist on type 'Option'.
            // Property 'bar' does not exist on type '{ name: "a"; foo: "foo"; }'.(2339)
            console.log(option.bar)
            break
    }
}

问题原因

TypeScript无法自动关联转换后的小写字符串和原联合类型的判别式,因为转换操作打破了TS对判别式值的追踪,导致类型缩小逻辑失效。你之前尝试的两种方法都没能建立这种关联:

  • printOptionToLowerCase中,toLowerCase(option.name)返回的是Lowercase<"a" | "b">,但TS无法将这个值和原option.name的具体值绑定,无法缩小option的类型。
  • printOptionAsserObjectField中的类型断言只是告诉TS转换后的值属于"a" | "b",但同样无法建立和原option的关联,类型缩小失败。

有效解决方法

方法1:自定义类型守卫函数

手动编写类型守卫,明确转换后的值对应的原联合类型成员:

const toLowerCase = <T extends string>(x: T) => x.toLowerCase() as Lowercase<T>;

type Option = {
    name: "a";
    foo: "foo"
} | {
    name: "b";
    bar: "bar"
}

// 自定义类型守卫:判断option是否对应指定name的类型
function isOptionWithName<T extends Option["name"]>(option: Option, name: T): option is Extract<Option, { name: T }> {
    return toLowerCase(option.name) === toLowerCase(name);
}

function printOptionFixed(option: Option) {
    if (isOptionWithName(option, "a")) {
        console.log(option.foo); // 正确推断类型,无错误
    } else if (isOptionWithName(option, "b")) {
        console.log(option.bar); // 正确推断类型,无错误
    }
}

方法2:使用映射类型预定义对应关系

创建映射类型,将小写name值映射到对应的联合类型成员,再通过类型断言辅助推断:

const toLowerCase = <T extends string>(x: T) => x.toLowerCase() as Lowercase<T>;

type Option = {
    name: "a";
    foo: "foo"
} | {
    name: "b";
    bar: "bar"
}

// 建立小写name到对应Option类型的映射
type LowercaseOptionMap = {
    [K in Lowercase<Option["name"]>]: Extract<Option, { name: Uppercase<K> }>;
};

function printOptionFixed2(option: Option) {
    const lowerName = toLowerCase(option.name);
    // 通过映射类型断言option的具体类型
    const typedOption = option as LowercaseOptionMap[typeof lowerName];
    
    if (lowerName === "a") {
        console.log(typedOption.foo); // 类型推断正确
    } else if (lowerName === "b") {
        console.log(typedOption.bar); // 类型推断正确
    }
}

内容的提问来源于stack exchange,提问作者ksantos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:00:02