如果要在类型系统中跟踪数组长度,可能需要使用tuple types,其中指定了数组元素的确切长度和顺序.因此,string[]
可以有任意数量的任意顺序的字符串,但["h", "e", "l"]
必须正好有三个元素,它们的值是字符串literals、"h"
、"e"
和"l"
.
假设你想支持messageList
的任何长度,那么你就不能真正写出Props
作为一个特定的类型;你需要像{messageList: [], messageTimeList: []} | {messageList: [string], messageTimeList: [number]} | {messageList: [string, string], messagetimeList: [number, number]} | ...
这样的无限union.相反,你可能希望Props
是T
中的generic,messageList
的类型,然后用tuple mapping来表示messageTimeList
应该是与T
相同的形状,只是它的元素应该是number
而不是string
.看起来是这样的:
interface Props<T extends string[]> {
initialMessage: string;
messageList: [...T];
messageTimeList: [...{ [I in keyof T]: number }];
}
注意,我在[... ]
中包装了messageList
和messageTimeList
属性;这是variadic tuple type,我在这里做这件事只是为了给编译器一个提示,让它推断T
的元组类型,而不是普通数组类型.
不管怎么说,Props<["h", "e", "l"]>
只允许["h", "e", "l"]
对messageList
,但messageTimeList
只允许[number, number, number]
.你不想手动写出Props<["h", "e", "l"]>
,幸运的是你不必...您可以将useFoo()
写入T
中的泛型,并在调用时让编译器为您推断T
:
function useFoo<T extends string[]>(
{ initialMessage, messageList, messageTimeList }: Props<T>
) {
if (messageList.length === messageTimeList.length) {
console.log('I want to must be same');
}
}
让我们来测试一下:
useFoo({
initialMessage: 'hello world',
messageList: ['h', 'e', 'l'],
messageTimeList: [1, 2] // error!
//~~~~~~~~~~~~~ <--
// Source has 2 element(s) but target requires 3.
});
useFoo({
initialMessage: "abc",
messageList: ['a', 'b', 'c'],
messageTimeList: [1, 2, 3]
}) // okay
看起来不错!
Playground link to code