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

如何通过联合类型参数区分TypeScript判别式联合类型?

问题:基于判别式联合类型的类型安全Getter实现

我有一个通过type属性区分的判别式联合类型HorribleObject,希望编写一个函数:接收联合类型参数GettableItemType,根据原对象是否为对应类型,返回正确类型的item或undefined。

手动编写单个类型的getter(如获取bar类型item)可正常工作,但类型较多时会产生大量冗余代码。尝试泛型实现时,由于参数是联合类型,编译器无法正确推断objectIGet的具体类型,无法直接返回正确类型的item。目前通过类型断言实现了正确的类型返回,但该方式不够规范,寻求更合适的实现方案。

相关代码示例

type HorribleObject = {
    type: "foo",
    item: {
        foo: string
    }
} | {
    type: "bar",
    item: {
        bar: number
    }
} | {
    type: "",
    item: Record<string, never>
}


let objectIGet: HorribleObject = {type:"", item:{}}

// 单个类型getter(可行)
const getBarItem = 'bar' in objectIGet.item ? objectIGet.item.bar : undefined;

// 泛型尝试(无法正确推断类型)
type GettableItemType = "foo" | "bar"
const getItemByType = (type: GettableItemType) => type in objectIGet.item ? objectIGet.item[type] : undefined;

当前类型断言实现(不够规范)

const getItemByType = <T extends GettableItemType>(type: T) => 
    type in objectIGet.item 
    ? (objectIGet.item as Record<T, Extract<HorribleObject, {item: {[K in T]: any}}>["item"][T]>)[type] 
    : undefined;

更规范的实现方案

方案1:自定义类型守卫

通过自定义类型守卫让编译器在分支中精准推断对象类型,避免复杂断言:

type HorribleObject = {
    type: "foo",
    item: {
        foo: string
    }
} | {
    type: "bar",
    item: {
        bar: number
    }
} | {
    type: "",
    item: Record<string, never>
}

let objectIGet: HorribleObject = {type:"", item:{}}

type GettableItemType = "foo" | "bar"

// 自定义类型守卫:判断对象是否包含指定类型的item
function isOfType<T extends GettableItemType>(obj: HorribleObject, type: T): obj is Extract<HorribleObject, { item: Record<T, any> }> {
    return type in obj.item;
}

const getItemByType = <T extends GettableItemType>(type: T) => {
    if (isOfType(objectIGet, type)) {
        return objectIGet.item[type];
    }
    return undefined;
};

// 类型推断验证
const fooItem = getItemByType("foo"); // 类型:string | undefined
const barItem = getItemByType("bar"); // 类型:number | undefined

方案2:精准定义返回类型

提前定义好对应类型的返回值类型,缩小断言范围,让代码更清晰:

type HorribleObject = {
    type: "foo",
    item: {
        foo: string
    }
} | {
    type: "bar",
    item: {
        bar: number
    }
} | {
    type: "",
    item: Record<string, never>
}

let objectIGet: HorribleObject = {type:"", item:{}}

type GettableItemType = "foo" | "bar"

// 定义对应类型的item类型
type ItemType<T extends GettableItemType> = Extract<HorribleObject, { item: { [K in T]: any } }>["item"][T];

const getItemByType = <T extends GettableItemType>(type: T): ItemType<T> | undefined => {
    return type in objectIGet.item ? (objectIGet.item as { [K in T]: ItemType<T> })[type] : undefined;
};

内容的提问来源于stack exchange,提问作者Michał Sadowski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:07:02