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"'.
这就是我想要实现的,让 TS 在元素无效时警告我
但是我需要防范其他数组的所有元素,而不是一个值,例如:
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"'.
看起来不错!
如果该数组元素存在于另一个数组中,您如何让打字稿在元素级别发出警告?
例如:
const canBeAorB = <A1 extends ("a" | "b")[]>(arr: A1) => arr
const arr0 = canBeAorB(["b", "c"]) // Type '"c"' is not assignable to type '"a" | "b"'.
这就是我想要实现的,让 TS 在元素无效时警告我
但是我需要防范其他数组的所有元素,而不是一个值,例如:
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"'.
看起来不错!