类型定义:如何键入数组以同时没有两个特定值

Type Definition : How to type an array to don't have tow specific value at the same time

我想创建一个只能具有这些特定值而没有其他值的类型:

[0,1],[1,1],[1,0],[1,-1],[0,-1],[-1,-1],[-1,0],[-1,1]

为了实现这一点,我做了这个类型定义:

type Steps = -1 | 0 | 1;
type Directions = [width:Steps, height:Steps];

但是有了这个定义,我可以做到:

const invalidDirection : Directions = [0,0];

而且我不知道如何限制它,尤其是 [0,0]

没有单一的、特定的tuple type that corresponds to your intended Directions. The single specific tuple type [width: Steps, height: Steps] will, by necessity, allow the first element (labeledwidth)独立于第二个元素(标记为height)被选择。因此,没有什么能阻止人们为这两个元素选择 0


如果你想让两个元素相互依赖,你应该把Directions写成union可接受的类型。您可以手动执行此操作,或者

type Directions = [width: Steps, height: Exclude<Steps, 0>] | 
  [width: Exclude<Steps, 0>, height: Steps];

type Directions = [width: 0, height: Exclude<Steps, 0>] | 
  [width: Exclude<Steps, 0>, height: 0] |
  [width: Exclude<Steps, 0>, height: Exclude<Steps, 0>];

type Directions = [width: 0, height: 1] | [width: 0, height: -1] | 
  [width: 1, height: 0] | [width: 1, height: 1] | 
  [width: 1, height: -1] | [width: -1, height: 0] | 
  [width: -1, height: 1] | [width: -1, height: -1];

(请注意,虽然所有这三个都对应于同一组静态 值,它们不被编译器认为是相互等价的,并且可能在不同的上下文中表现出不同的行为;参见 进行一些讨论)

任何这些都会导致所需的行为:

let dir: Directions;
dir = [0, -1]; // okay
dir = [1, -1]; // okay
dir = [1, 0]; // okay
dir = [1, 1]; // okay
dir = [0, 1]; // okay
dir = [-1, 1]; // okay
dir = [-1, 0]; // okay
dir = [-1, -1]; // okay
dir = [0, 0]; // error!

前两种方法使用 the Exclude<T, U> utility type 从联合中过滤类型。请注意,第一个足够简洁,您可以决定直接使用它。


另一方面,您可能希望根据 Steps 的定义 以编程方式 执行此操作,而不是手动...尤其是当您关心缩放工会或改变他们的价值观。这是一种方法:

type Directions = Exclude<
    { [W in Steps]: {
        [H in Steps]: [width: W, height: H] }[Steps]
    }[Steps], [0, 0]>;

我们正在 Exclude-ing 表达式 {[W in Steps]....} 中的坏 [0, 0] 成员,所以让我们检查一下。在这里,我正在制作一个嵌套的“分布式对象类型”(如 microsoft/TypeScript#47109), which is making a mapped type and then immediately indexing into it 中定义的那样,以获得联合。让我们看一下内部的,{[H in Steps]: [width: W, height: H]}[Steps]:

映射类型在 Steps 的每个联合成员上迭代 H 并创建一个新的 three-keyed 对象类型,如 {"-1": [width: W, height: -1], "0": [width: W, height: 0], "1": [width: W, height: 1]}。然后我们使用索引 [-1 | 0 | 1] 索引到该对象类型,它产生 three-member 联合 [width: W, height: -1] | [width: W, height: 0] | [width: W, height: 1]。您有望看到外部分布式对象类型如何为 W 插入 -101 并生成 nine-element 联合。

回想一下,我们 Exclude 来自该联合的 [width: 0, height: 0] 元素以获得您想要的 eight-element 联合!

Playground link to code