假设我有function foo(args) {...}
,其中args
是一个二元组数组,这样元组中的条目是相同类型的(即[T,T]
),但是跨元组的条目可能任意变化(即[[T,T], [U,U],[V,V]]
)。例如:
foo([ [1, 3], ["hello", "world"], [true, true], [2, 7] ]) // no error
我应该如何输入 foo
的 args
参数,以便元组中的不匹配类型引发编译时类型错误?例如:
foo([ [1, 3], ["hello", 5], // type error here [true, true], [2, 7n] // type error here ])
如果无法内联显示类型错误,则使整个函数调用错误也是可以接受的。
附录:是否可以使用 [SomeType
类型的 2 元组(即第二个条目的类型应与第一个),但 T 仍然可以在元组之间变化 [[SomeType
?
foo([ [{value: 1}, 3], [{value: "hello"}, 5], // type error here [{value: true}, true], [{value: 2}, 7n] // type error here ])
我认为您可以通过为
row
创建一个类型来简单地实现此目的,该类型将接受string
、number
或 布尔值。类型 Row = string[] |布尔值[] |数字[]
现在,我们可以为
foo
函数的args
参数分配此类型。使用此类型定义,如果您向
foo
提供一个参数,其中行中元素的类型不匹配,Typescript 将引发错误。这里是游乐场
链接代码>
.为了实现这一点,我们需要使用 泛型数组和映射类型来映射数组的元素。由于我们知道该数组应该是长度为 2 的元组数组,因此我们将推断元组中第一项的泛型参数,并使第二项具有相同类型。要获取泛型参数的类型,我们需要使用 推断关键字。请注意,我们需要确切地知道(或至少是具有相似形状的类型)用于使其工作的泛型类型,在我们的例子中是
Variable
:看起来似乎就是全部,但是让我们看看以下数组的类型:
如您所见,该类型与我们在 arr 中的类型不完全相同。编译器扩展了类型以确保我们可以改变数组元素。为了让编译器知道该数组是只读的,我们需要使用 const 断言:
现在看起来不错,这意味着我们需要将传递给
foo
的数组设置为只读`,并且由于只读数组是我们将得到的可变数组的超集如果我们尝试将只读数组传递给数组,则会出现错误:因此,我们将
foo
中的所有数组类型更新为只读。请注意,由于我们的数组是二维的,因此内部数组也将是只读的,并且数组的约束应该是只读数组的只读数组:测试:
但是,我们仍然存在一些问题。例如,如果元组中的第一个元素是
Variable
,则意味着第二个参数也应该是7
,而不是任何数字,如果这是一个问题我们需要获取7
的原语,即数字。这可以使用 ToPrimitive 来自我的 type-samurai 开源项目的实用程序类型: p>更新功能:
另一个问题是,如果在我们当前的
foo
实现中推断的类型是number[]
,我们将不会允许只读数组:修复非常简单,我们将检查推断的类型是否是某个数组,然后获取其元素类型并将 readonly ElemenType[] 作为元组中的第二个参数:
测试:
令人烦恼的部分是我们需要在任何地方使用
const 断言
。在 Typescript5.0
中,const 类型参数,这样我们就可以避免const 断言
:不幸的是,我们无法使用它们,因为我们对参数进行了一些操作,而不是直接将
T
作为类型分配给它:总之,目前,
const 断言
是确保其按预期工作的唯一方法。链接到游乐场