TS:确保对象具有字符串联合类型的单个键?

时间:2019-12-13 14:49:10

标签: typescript

我正在尝试构建一个看起来像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: [],
};

正如您在操场上看到的那样,它并不完全符合我想要的方式: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=18&pc=3#code/C4TwDgpgBA8pBOBDYB7eUC8UDkBBAcgCLZQA+OMAStgNwBQdoksCya+KAJtFgN5QBtGFACWAOxYQkqeAF0AXINlQAvvToBjFGIDOwKDoAWKAK4AbTgHU0AawCMiuFLbwO3TFF50oUAoUUCsgA0dGoMWrr6RqYW1vA2AEyOrDJuPJ7esJQBwaHqEXoGxuacAGKIImYOktLsXOm8YZrahdEl5ZVJNS5pHl4+fjkhPlRDeUA

据我所知,由于结构化类型的实现,这可能会很复杂,但是也许有技巧?

1 个答案:

答案 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>>

是的,看起来不错。希望能有所帮助;祝你好运!

Link to code