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

如何声明以domain为键且值域覆盖domain的TypeScript对象类型?

实现TypeScript中的满射对象类型

首先需要让TypeScript识别出domain的具体字面量元素类型,而不是宽泛的string[],可以用as const将数组转为只读字面量数组:

const domain = ["A", "B", "C"] as const;
type Domain = typeof domain[number]; // 得到联合类型 "A" | "B" | "C"

接下来定义满射映射类型,要求对象的键完全来自Domain,且值的集合必须覆盖整个Domain:

// 满射映射类型:键为T的所有成员,值属于T类型,且值的联合需覆盖全部T成员
type SurjectiveMap<T> = Record<T, T> & (
  // 检查T中是否有成员未出现在值的联合中
  Exclude<T, Record<T, T>[keyof Record<T, T>]> extends never ? unknown : never
);

验证示例

// 类型正确:值集合{"B", "C", "A"}覆盖了Domain所有成员
const obj1: SurjectiveMap<Domain> = {
  A: "B",
  B: "C",
  C: "A",
};

// 类型错误:值集合{"B", "C"}缺少"A",类型检查报错
const obj2: SurjectiveMap<Domain> = {
  A: "B",
  B: "C",
  C: "B",
};

原理说明

  • Record<T, T>确保对象的每个键都是T的成员,值也属于T类型;
  • Exclude<T, Record<T, T>[keyof Record<T, T>]>会找出T中未出现在对象值的联合类型里的成员。如果所有成员都被覆盖,结果为never,条件成立,类型有效;否则结果是缺失的成员,条件不成立,类型变为never,无法完成赋值。

可选:用泛型函数约束

如果觉得直接用类型注解不够直观,也可以通过泛型函数来实现约束:

function createSurjectiveMap<T extends Domain>(
  map: Record<Domain, Domain> & (Exclude<Domain, T> extends never ? unknown : never)
): typeof map {
  return map;
}

// 正确
const obj1 = createSurjectiveMap({
  A: "B",
  B: "C",
  C: "A",
});

// 错误:类型检查报错
const obj2 = createSurjectiveMap({
  A: "B",
  B: "C",
  C: "B",
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:40:32