TypeScript如何仅允许对象中键的特定组合?
问题:在TypeScript中定义严格约束的不等式对象类型
我想在TypeScript中定义一个表示不等式的对象类型,合法示例如下:
{ "<": x => Math.sin(x) } // 合法,y < sin(x) { ">": x => Math.sin(x) } // 合法,y > sin(x) { ">=": -4, "<": 4 } // 合法,-4 <= y < 4
但需要禁止无意义的组合,比如同时包含"小于"和"小于等于":
{ "<": 5, "<=": 5 } // 应触发TypeScript错误
我尝试了联合类型方案,但TypeScript会合并所有类型,允许任意键组合:
type Fn = number | ((x: number) => number) type Inequality = | Record<">", Fn> | Record"<", Fn> | Record"<=", Fn> | Record">=", Fn> | Record<">" | "<", Fn> | Record<">" | "<=", Fn> | Record">=" | "<", Fn> | Record">=" | "<=", Fn>
是否可以让TypeScript仅允许我声明的键组合?
解决方案
之前的Record联合类型失效的核心原因是:Record<K, T>允许对象包含额外键,TypeScript的结构类型系统会认为"包含更多键的对象兼容目标类型"。要实现严格约束,需要明确禁止冲突键存在。
可以通过**互斥类型结合never**来实现,以下是两种可行写法:
写法1:分组约束(简洁版)
先将互斥的操作符分组,再定义允许的组合:
type Fn = number | ((x: number) => number); // 互斥操作符分组:小于组、大于组 type LessOp = "<" | "<="; type GreaterOp = ">" | ">="; // 单个操作符的合法类型 type SingleInequality = | { [K in LessOp]: Fn } | { [K in GreaterOp]: Fn }; // 跨组组合的合法类型(仅允许一个大于组操作符 + 一个小于组操作符) type CombinedInequality = | { [K1 in GreaterOp]: Fn } & { [K2 in LessOp]: Fn }; // 最终的不等式类型 type Inequality = SingleInequality | CombinedInequality;
写法2:精确约束(严谨版)
通过& { [K in 冲突键]?: never }明确禁止冲突键,确保对象仅包含允许的键:
type Fn = number | ((x: number) => number); type Inequality = // 单个大于操作符,禁止所有小于组操作符 | ({ ">": Fn } & { [K in "<" | "<="]?: never }) | ({ ">=": Fn } & { [K in "<" | "<="]?: never }) // 单个小于操作符,禁止所有大于组操作符 | ({ "<": Fn } & { [K in ">" | ">="]?: never }) | ({ "<=": Fn } & { [K in ">" | ">="]?: never }) // 跨组组合:大于组 + 小于组,禁止其他冲突键 | ({ ">": Fn; "<": Fn } & { [K in ">=" | "<="]?: never }) | ({ ">": Fn; "<=": Fn } & { [K in ">=" | "<"]?: never }) | ({ ">=": Fn; "<": Fn } & { [K in ">" | "<="]?: never }) | ({ ">=": Fn; "<=": Fn } & { [K in ">" | "<"]?: never });
验证效果
// 合法示例(无TS错误) const valid1: Inequality = { "<": x => Math.sin(x) }; const valid2: Inequality = { ">": x => Math.sin(x) }; const valid3: Inequality = { ">=": -4, "<": 4 }; // 非法示例(触发TS错误) const invalid1: Inequality = { "<": 5, "<=": 5 }; // 同时包含小于/小于等于,冲突 const invalid2: Inequality = { ">": 3, ">=": 3 }; // 同时包含大于/大于等于,冲突
内容的提问来源于stack exchange,提问作者Steven Petryk
相关产品推荐
相关产品推荐

