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

如何在TypeScript中构造满足{} | A | B | (A&B)的接口部分和类型?

构造TypeScript的离散接口「部分和」类型

我需要构造一个TypeScript类型S,使其成为若干离散接口(比如示例中的A和B)的「部分和」,即类型为{} | A | B | (A & B)。

我尝试了以下代码,但结果不符合预期(问题可能出在{}并非真正的空接口):

type A = {
    readonly a1: 1
}

type B = {
    readonly b1: 1
    readonly b2: 2
}

type Maybe<T> = T | {}

type S =  Maybe<A> & Maybe<B>
// 预期代数类型: C = ({} & {}) | (A & {}) | (A & B) | ({} & B)

const value1: S = {} // 应该通过类型检查(不包含A或B的属性)
const value2: S = {a1: 1} // 应该通过类型检查(包含A的属性)
const value3: S = {b1: 1, b2: 2} // 应该通过类型检查(包含B的属性)
const value4: S = {a1: 1, b1: 1, b2: 2} // 应该通过类型检查(同时包含A和B的属性)
const value5: S = {d: 1} // 理想情况下不应该通过类型检查(`d`是多余属性)
const value6: S = {a1: 2} // 不应该通过类型检查(不符合A的类型要求)

我已经考虑过Partial,但Partial<A> & Partial<B>并不等价于{} | A | B | (A & B),无法满足需求。请问能否实现这一类型?


解决方案

要实现这个「部分和」类型,核心是精确约束只能存在目标接口的完整属性集、空对象,且不允许多余属性。可以通过以下方式实现:

// 工具类型:确保对象只能包含指定类型的键,多余键会触发错误
type Exact<T, U extends T> = U & Record<Exclude<keyof U, keyof T>, never>;

type A = {
    readonly a1: 1
}

type B = {
    readonly b1: 1
    readonly b2: 2
}

// 直接枚举所有合法情况:空对象 | 完整A | 完整B | 完整A+B
type S = 
  | {} 
  | Exact<A, A> 
  | Exact<B, B> 
  | Exact<A & B, A & B>;

// 测试验证
const value1: S = {}; // ✅ 通过
const value2: S = {a1: 1}; // ✅ 通过
const value3: S = {b1: 1, b2: 2}; // ✅ 通过
const value4: S = {a1: 1, b1: 1, b2: 2}; // ✅ 通过
const value5: S = {d: 1}; // ❌ 报错:类型 { d: 1; } 不能赋值给类型 S
const value6: S = {a1: 2}; // ❌ 报错:类型 2 不能赋值给类型 1

原理说明

  • Exact<T, U extends T>:这个工具类型通过Record<Exclude<keyof U, keyof T>, never>约束,确保U的键只能来自T,多余的键会被标记为never,从而触发类型错误。结合Exact<A, A>可以强制必须完整符合A的结构,同理处理B和A&B。
  • 直接枚举{} | A | B | (A&B)并通过Exact约束,就能严格实现需求:只允许空对象、完整的A、完整的B,或同时完整的A+B,且不允许多余属性或不完整的接口属性。

如果需要支持更多接口的扩展,可以封装成通用工具类型:

type Exact<T, U extends T> = U & Record<Exclude<keyof U, keyof T>, never>;

// 通用部分和类型:接收接口列表,生成空对象、单个完整接口、任意组合完整接口的联合类型
type PartialSum<T extends object[]> = 
  | {} 
  | { [K in keyof T]: Exact<T[K], T[K]> }[number]
  | { [K in keyof T]: Exact<T[K], T[K]> }[number] & { [K in keyof T]: Exact<T[K], T[K]> }[number];

// 使用示例
type S = PartialSum<[A, B]>;

后续添加新接口时,只需修改PartialSum的参数数组即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 20:15:18