我有一个可以提供给函数的参数列表'one' | 'two' | 'three' | 'four' | 'five'。我希望每个只使用一次,如下所示:
function foo(...x: ('one' | 'two' | 'three' | 'four' | 'five')[]) {
// do something...
}
foo('one', 'five', 'three', 'five'); // This works, even though I want TypeScript to say this isn't allowed.
Run Code Online (Sandbox Code Playgroud)
评论是正确的:几乎不可能保证只有唯一的数组才能通过,因此无论您如何处理输入,都应该有运行时代码来消除传入的任何数组的重复项。
不过,您可能希望键入内容向用户提示您最多只能传入每个参数类型一次。由于 TypeScript 4.1 对递归条件类型的支持,这是可能的,甚至不是完全疯狂:
type UniqueFrom<T extends any[], U> =
U[] extends T ? T :
T extends [infer F, ...infer R] ?
[U, ...UniqueFrom<R, Exclude<U, F>>] : []
type Vals = 'one' | 'two' | 'three' | 'four' | 'five';
function foo<T extends Vals[]>(...t: UniqueFrom<T, Vals>) { }
Run Code Online (Sandbox Code Playgroud)
这个想法是UniqueFrom<T, Vals>应该生成一个与 长度相同的元组,显示当您从左到右遍历时,每一步中T哪些元素仍然可用。例如:ValsT
type U = UniqueFrom<["one", "two", "three"], Vals>;
// [Vals, "two" | "three" | "four" | "five", "three" | "four" | "five"]
Run Code Online (Sandbox Code Playgroud)
这表示 的第一个元素T可以是以下任意内容Vals;一旦第一个元素被选择为"one",现在下一个元素可以是除了Vals之外的任何元素"one"。当第二个元素被选择为 时"two",现在最后一个元素可以是"three"、"four"或中的任何元素"five"。
只要T可以分配给UniqueFrom<T, Vals>,那么编译器就会接受类型的元组T作为 的剩余参数foo()。让我们看看它是否有效:
foo("one", "two", "three", "four", "five"); // okay
foo("two", "one", "six", "five"); // error!
// -------------> ~~~~~
// type '"six"' is not assignable to type 'Vals'
foo("one", "two", "three", "two", "five"); // error!
// ----------------------> ~~~~~
// type '"two"' is not assignable to type '"four" | "five"'
Run Code Online (Sandbox Code Playgroud)
这对我来说看起来很合理。编译器会抱怨第二个参数名为"two",并告诉您它需要其中一个"four"或"five"一个。