如何扩展含联合判别字段的TypeScript判别式联合类型?
问题描述
我定义了如下TypeScript判别式联合类型:
type Union = { // 重点!判别字段的值本身也可以是联合类型! type: "foo" | "bar" } | { type: "baz" } | { type: "quux" }
我希望扩展其中type为"baz"的成员,添加额外属性(例如value: string),最终得到目标类型。同时希望该工具能支持同时扩展联合中的多个类型:当扩展联合判别字段中的单个值时,需为对应含联合type的成员添加属性;同时扩展多个值时,需合并对应属性。
我自行实现了Extend工具类型,但它无法处理type为"foo" | "bar"的成员,会直接将其从结果中省略:
type Extend<TTypeOverride extends { [key in Union["type"]]?: unknown }> = { [key in Union["type"]]: // 此处Extract是问题所在,无法提取type为联合类型的成员 Extract<Union, { type: key }> & (TTypeOverride[key] extends undefined ? unknown : TTypeOverride[key]) }[Union["type"]] // 结果中不包含{ type: "foo" | "bar" } type ExtendedUnion = Extend<{ quux: { value: string } }>
请问该如何解决Extract无法处理联合判别字段的问题?
解决方案
核心问题是Extract<Union, { type: key }>的匹配逻辑有误:当key是"foo"或"bar"时,它会去原联合里找type严格等于key的成员,但原联合第一个成员的type是"foo"|"bar",并不满足严格等于单个值的条件,所以返回never,最终导致这个成员被过滤。
我们需要换一种匹配思路:遍历原联合的每个成员而非type的枚举值,判断当前成员的type是否包含要扩展的key,这样就能保留原有的联合type结构。
修正后的工具类型实现
type Extend<TTypeOverride extends { [key in Union["type"]]?: unknown }> = Union extends infer U ? U extends { type: infer Type } // 匹配当前成员type对应的扩展配置 ? U & (TTypeOverride[Extract<Type, Union["type"]>] extends undefined ? unknown : TTypeOverride[Extract<Type, Union["type"]>]) : never : never;
验证效果
- 扩展单个成员:
type ExtendedUnion = Extend<{ quux: { value: string } }>; // 输出结果: // { type: "foo" | "bar" } | { type: "baz" } | { type: "quux"; value: string }
type:"foo"|"bar"的成员被完整保留,quux成员成功添加了value属性。
- 扩展多个成员(包括联合type中的单个值):
type ExtendedUnion2 = Extend<{ foo: { fooProp: number }, baz: { bazProp: boolean } }>; // 输出结果: // ({ type: "foo" | "bar" } & { fooProp: number }) | { type: "baz"; bazProp: boolean } | { type: "quux" }
因为原成员的type是"foo"|"bar",扩展foo会给整个成员添加fooProp属性——如果需要只给type="foo"的分支单独扩展,需要先把原联合拆分为每个type单独的成员再操作;若要保留原有的联合type结构,上述方案完全满足需求。
内容的提问来源于stack exchange,提问作者Applekini
相关产品推荐
相关产品推荐

