为什么元组联合期望`never`作为`.includes()`参数?
type Word = "foo" | "bar" | "baz";
const schema = {
foo: ["foo"] as const,
bar: ["bar"] as const,
baX: ["bar", "baz"] as const,
};
const testFn = (schemaKey: keyof typeof schema, word: Word) => {
const array = schema[schemaKey]
array.includes(word);
// ^
// TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
// Type 'string' is not assignable to type 'never'
}
游乐场链接
为什么会发生这种情况?这对我来说似乎很愚蠢……还是我遗漏了什么?我如何合理地解决这个问题?我只是断言从不,还是有隐藏的警告?
回答
让我们来看看其中的一些类型。我们有
array: readonly ["foo"] | readonly ["bar"] | readonly ["bar", "baz"]
现在, a 上的include方法T[]采用 typeT的参数,即列表的正确类型的参数。这意味着,在 Typescript 中,询问字符串是否是整数数组的元素并不是很好的类型,因为这种代码很可能一开始就是错误的。
现在,我们没有数组。我们有工会。如果该方法适用于所有联合选项,则只能在联合上调用该方法。
["foo"]有一个include将 a"foo"作为参数的方法(即,唯一存在的类型是文字 string "foo"。同样,["bar"]有一个include将 a"bar"作为参数的方法,并且["bar", "baz"]有一个include将类型的元素"bar" | "baz"作为参数的方法。
但我们不知道我们有哪些。我们可以拥有其中任何一个。所以我们include必须采取对所有这三个都有效的东西。也就是说,我们include必须接受一个类型为
"foo" & "bar" & "baz"
所以我们可以传递与上述所有三个字符串相等的任何字符串。我不知道任何这样的字符串,Typescript 编译器也不知道,所以它正确地说该类型是无人居住的,即never. 没有类型安全的调用方式include。
你可以any-cast 你的方式绕过它,就像你可以在 Typescript 中的所有东西一样。但更聪明的技术是保持类型安全。看,Typescript 推断的类型对于您的变量来说太具体了array,这是您开始处理文字类型时的常见问题。但似乎你想要的类型是
array : readonly Word[]
碰巧,它是我在本文顶部指出的类型的完全有效的超类型。我们可以简单地使用显式类型声明来使代码工作
const array: readonly Word[] = schema[schemaKey]
请注意,这不是演员表。我们不会告诉类型系统“我比你更了解,嘘”。readonly Word[]是 Typescript 推断的字面联合的完全有效的超类型。这段代码仍然是类型检查和类型安全的。我们只需要给编译器一些关于我们意图的提示。