应该如何定义
MyType
以确保我的数组中的每个项目都是由相同类型的项目组成的数组?
const array1: MyType = [["foo", "bar"], [42, 42], [true, false]]; // OK
const array2: MyType = [["foo", "bar"], [42, 42], [true, "false"]]; // Should throw a TS error
在我的示例中,我使用了
string
、number
和 boolean
,但它可以是任何东西。
TypeScript 中没有特定类型以这种方式工作。从概念上讲,您希望拥有一个存在量化的泛型类型,例如
// don't write this, it doesn't work
type MyType = Array< <exists T>Array<T> >
说
MyType
是一个数组的数组,其中每个元素都是 some 数组类型。好吧,这也不太有效,因为 [true, "false"]
是数组类型 Array<true | "false">
,所以大概您需要尝试引导推理远离第一个数组之后的每个数组的元素,也许使用 NoInfer
实用型喜欢
// don't write this, it doesn't work
type MyType = Array< <exists T>[T, ...NoInfer<T>[]] >
但无论哪种方式都行不通。 TypeScript 不直接支持存在类型,你不能说“某种类型”。你只能通过使用通用量化泛型来说“适用于所有类型”,这是所有 TypeScript(以及大多数其他具有泛型的语言)支持的。
这意味着你必须使
MyType
变得通用。喜欢:
type MyType<T extends unknown[]> = { [I in keyof T]: readonly [T[I], ...NoInfer<T[I]>[]] }
现在你可以写了
const array1: MyType<[string, number, boolean]> =
[["foo", "bar"], [42, 42], [true, false, false]]; // okay
const array2: MyType<[string, number, boolean]> =
[["foo", "bar"], [42, 42], [true, "false"]]; // error
但这是多余的。你被迫写出
[string, number, boolean]
。如果 TypeScript 能够为您“推断”这一点,那就太好了。不幸的是,这不是泛型类型的工作方式。 microsoft/TypeScript#32794 有一个功能请求,如果实现,可能意味着您可以编写 const array1: MyType<infer> = ⋯
,但目前它不是语言的一部分,因此您也需要解决这个问题。
TypeScript 仅在调用泛型时推断泛型类型参数。因此,我们可以编写一个辅助函数,它只在运行时返回其输入,但会为您提供所需的推论,如下所示:
const myType = <T extends unknown[]>(t: MyType<T>) => t;
const array1 = myType([["foo", "bar"], [42, 42], [true, false, false]]); // okay
const array2 = myType([["foo", "bar"], [42, 42], [true, "false"]]); // error!
看起来不错。
注意,
array1
和
array2
的类型与之前相同,但现在不需要注释。另请注意,const arr = myType([⋯])
实际上并不比const arr: MyType = [⋯]
更难编写,因此即使您更喜欢注释而不是调用辅助函数,但希望它不会太繁重。Playground 代码链接