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

如何提取TypeScript中符合{id:string}的动态oneOf类型并创建类型守卫?

TypeScript提取含id的联合类型及类型守卫实现

一、正确提取符合条件的类型

你原来的写法无法生效,是因为Foo[K]会返回整个Foo联合类型,而非对应$case值的具体成员。需要用Extract工具类型筛选联合类型中符合条件的成员:

type Foo =
    | { $case: 'a'; a: { id: string } }
    | { $case: 'b'; b: { message: string } }
    | { $case: 'c'; c: { message: string } }
    | { $case: 'd'; d: { id: string } }
;

type Cases = Foo['$case'];

// 筛选出对应case属性包含id的Foo成员
type FooWithId = Extract<Foo, { $case: infer K } & Record<K, { id: string }>>;
// 提取这些case的名称类型
type CasesWithId = FooWithId['$case']; // 类型为 'a' | 'd'

原理说明

Extract会遍历Foo的每个联合成员,检查是否满足:

  1. 存在$case字段(通过{ $case: infer K }推断出当前case的名称K)
  2. 同时存在一个与K同名的键,且该键的类型继承自{ id: string }(通过Record<K, { id: string }>约束)

符合条件的成员会被保留到FooWithId中,之后就可以提取对应的case名称类型CasesWithId。

二、实现类型守卫函数

要实现hasId类型守卫,需要通过运行时检查确认当前Foo实例属于FooWithId,同时给TypeScript提供类型断言:

function hasId(foo: Foo): foo is FooWithId {
    const caseKey = foo.$case;
    // 检查对应case的属性是否存在id字段且为字符串类型
    return typeof (foo as any)[caseKey]?.id === 'string';
}

使用示例

function handleFoo(currentCase: Foo) {
    if (hasId(currentCase)) {
        // 此处currentCase被TypeScript推断为FooWithId类型
        // 安全访问对应case属性的id
        return currentCase[currentCase.$case].id;
    } else {
        // 此处currentCase为'b'或'c'类型
        return currentCase[currentCase.$case].message;
    }
}

注意:原类型中id是嵌套在对应case的属性里的,所以无法直接访问currentCase.id(原类型结构中不存在该字段)。如果需要简化访问,可以调整类型定义为扁平结构,比如将id直接放在顶层,但按照现有结构,上述写法是完全类型安全的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:50:47