我正在尝试构建一个看起来像MongoDB查询的类型安全查询系统。
我想保证一个对象具有联合类型的单个属性,但是找不到解决方案。有人可以帮我编译以下代码吗?
type Operator = 'AND' | 'OR';
type OperatorNode = { [O in Operator]: [] };
const shouldWork1: OperatorNode = {
AND: [],
};
const shouldWork2: OperatorNode = {
OR: [],
};
const shouldFail1: OperatorNode = {};
const shouldFail2: OperatorNode = {
AND: [],
OR: [],
};
据我所知,由于结构化类型的实现,这可能会很复杂,但是也许有技巧?
答案 0 :(得分:6)
您想要的最终类型是
type OperatorNode = { AND: []; OR?: undefined; } | { OR: []; AND?: undefined; }
也就是说,类型的并集,每个类型都有一个已定义的属性,而其余的属性(如果存在的话)仅被允许为undefined
。您可以验证它是否按预期工作:
const shouldWork1: OperatorNode = { AND: [], }; // okay
const shouldWork2: OperatorNode = { OR: [], }; // okay
const shouldFail1: OperatorNode = {}; // error!
const shouldFail2: OperatorNode = { AND: [], OR: [], }; // error!
以编程方式将Operator
转换为OperatorNode
需要一些类型的变戏法。这是我的处理方法:
type ExclusiveRecord<K extends PropertyKey, V> = {
[P in K]: Record<P, V> & Partial<Record<Exclude<K, P>, never>> extends
infer O ? { [Q in keyof O]: O[Q] } : never }[K]
这使用几个mapped types遍历联合P
的每个元素K
并构造Record<P, V> & Partial<Record<Exclude<K, P>, never>>
。 Record<P, V>
表示它具有值为P
的{{1}}键,而V
表示Partial<Record<Exclude<K, P>, never>>
中除K
以外的每个键都没有定义价值观。关于P
的地方只是将丑陋的交集变成单个对象类型,因此不必强制处理extends infer O...
。然后将所有这些最后结合在一起。让我们确保它有效:
Record<"AND", []> & Partial<Record<"OR", never>>
是的,看起来不错。希望能有所帮助;祝你好运!