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

如何在TypeScript中筛选包含指定字段的联合类型成员

从区分联合类型中筛选包含指定字段的成员

我有一个区分联合类型,其中多个成员包含一个非区分字段:

interface Foo {
  type: 'foo';
  foo: string;
}

interface OptionalFoo {
  type: 'optionalfoo';
  foo?: string;
}

interface RequiredNullableFoo {
  type: 'requirednullable';
  foo: string | null | undefined;
}

interface Bar {
  type: 'bar';
  bar: string;
}

type All = Foo | OptionalFoo | RequiredNullableFoo | Bar;

我希望定义一个类型工具UnionMembersWithField,从该联合类型中筛选出包含指定字段的成员,最终得到:

type Foos = UnionMembersWithField<All, 'foo'>;
// Foos = Foo | OptionalFoo | RequiredNullableFoo;

我尝试了几种写法,但都无法完全满足需求:

// 工具类型:`keyof All` 为 `'type'`,因它是唯一公共字段,但我们希望接受联合类型任意成员的字段
type KeysOfUnion<T> = T extends T ? keyof T : never;

// 该定义得到 `Foo | RequiredNullableFoo`,遗漏了 `OptionalFoo`
type UnionMembersWithField<T, K extends KeysOfUnion<T>> = T extends Record<K, any> ? T : never;

// 该定义因TS1170错误无法编译,但最贴合需求表达
type UnionMembersWithField<T, K extends KeysOfUnion<T>> = T extends { [K]?: any } ? T : never;

// 该定义返回`never`...
type UnionMembersWithField<T, K extends KeysOfUnion<T>> = Required<T> extends Record<K, any> ? T : never;

// ...这超出预期,因为用辅助类型`DiscriminateUnion`重新定义后,得到
// `Required<Foo> | Required<OptionalFoo> | Required<RequiredNullableFoo>`
type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = T extends Record<K, V> ? T : never;
type UnionMembersWithField<T, K extends KeysOfUnion<T>> = DiscriminateUnion<Required<T>, K, any>;

其余尝试要么无效果(输出类型与输入类型一致),要么退化为never。

考虑到All['foo']的类型为any(当并非所有联合成员都包含指定字段时,TypeScript无法生成合理类型),我甚至不确定是否能实现这样的类型。


解决方案

要实现这个需求,我们可以利用TypeScript的条件类型分布式特性,结合keyof判断字段是否存在(包括可选字段)。正确的UnionMembersWithField定义如下:

type KeysOfUnion<T> = T extends T ? keyof T : never;

type UnionMembersWithField<T, K extends KeysOfUnion<T>> = 
  T extends unknown 
    ? K extends keyof T 
      ? T 
      : never 
    : never;

原理说明

  • 分布式条件类型:T extends unknown会触发联合类型的分发,让每个成员单独参与判断
  • K extends keyof T判断当前联合成员是否包含字段K,这里的keyof T会包含可选字段(比如keyof OptionalFoo是'type' | 'foo')
  • 这样就能准确筛选出所有包含指定字段(包括可选和可空的必填字段)的联合成员

测试验证:

type Foos = UnionMembersWithField<All, 'foo'>;
// 类型为 Foo | OptionalFoo | RequiredNullableFoo,符合预期

如果需要更灵活的参数限制,也可以让K直接接受PropertyKey类型,不需要依赖KeysOfUnion:

type UnionMembersWithField<T, K extends PropertyKey> = 
  T extends unknown 
    ? K extends keyof T 
      ? T 
      : never 
    : never;

这样即使K不在联合的公共字段里,只要存在于某个成员中,也能正确完成筛选。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 20:26:16