如何声明以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
相关产品推荐
相关产品推荐

