Typescript - 防止交叉元素

Typescript - Guard against intersection elements

如果该数组元素存在于另一个数组中,您如何让打字稿在元素级别发出警告?

例如:

const canBeAorB = <A1 extends ("a" | "b")[]>(arr: A1) => arr

const arr0 = canBeAorB(["b", "c"]) // Type '"c"' is not assignable to type '"a" | "b"'.

这就是我想要实现的,让 T​​S 在元素无效时警告我

但是我需要防范其他数组的所有元素,而不是一个值,例如:

const blockedElements = ["a", "b"]

const myArr = ["b", "c"] // make TS yell that "b" is invalid because its a member of blockedElements 

我这样做真的很接近:

const blockedValues = ["a", "b"] as const;

type BlockedElement = typeof blockedValues[number];

const cannotHaveBlockedEls = <A1 extends Readonly<Exclude<A1[number], BlockedElement>[]>>(arr: A1) => arr

const arr3 = cannotHaveBlockedEls(["b", "c"] as const)

将产生:

Argument of type 'readonly ["b", "c"]' is not assignable to parameter of type 'readonly "c"[]'.
  Type '"b" | "c"' is not assignable to type '"c"'.
    Type '"b"' is not assignable to type '"c"'

但是错误显示在整个数组参数的顶部而不是“b”的顶部,我的实际用例是一个更大的数组,这确实会有所不同。

可以吗?

您 运行 遇到的情况是 ["b", "c"] as const 中的 const assertion 实际上使整个表达式对您传递给它的 cannotHaveBlockedEls() 函数不透明.就好像你这样写:

const oops = ["b", "c"] as const;
const arr3 = cannotHaveBlockedEls(oops);
//                                ~~~~ <-- error

大概你首先在那里写 as const 的唯一原因是强制编译器将 ["b", "c"] 视为字符串数组 literal types 而不仅仅是 string[].

如果是这样,还有其他方法可以做到这一点。这是一个:

const cannotHaveBlockedEls = <T extends string>(arr: Exclude<T, BlockedElement>[]) => arr

字符串文字类型的generic type parameter T is constrained to string, which gives the compiler a hint that you'd like it to be inferred as a string literal type (or a union)。由于 T 现在是 string,我们需要 arr 是一个元素类型为 Exclude<T, BlockedElement>.

的数组类型

当然这意味着arr必须是字符串数组或字符串文字。如果您需要接受其他类型,则必须扩大对 T.

的约束

无论如何,让我们看看实际效果:

const arr3 = cannotHaveBlockedEls(["b", "c"]); // error!
// ------------------------------> ~~~
// Type '"b"' is not assignable to type '"c"'.

看起来不错!