如何提取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的每个联合成员,检查是否满足:
- 存在
$case字段(通过{ $case: infer K }推断出当前case的名称K) - 同时存在一个与
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
相关产品推荐
相关产品推荐

