问题描述
我正在努力理解如何在 Typescript 中使用 variadic tuple types。我试图通读文档和 GitHub 上的一些问题,但示例总是比“超级基本”更奇怪,所以我想知道是否有人可以帮我输入几个“超级基本”的,这样我就可以了也许用这些来“得到它”。
假设我们有这些功能:
function wrap(...items) {
return items.map(item => ({ value: item }))
}
function wrapArray(items) {
return items.map(item => ({ value: item }))
}
function unwrap(...items) {
return items.map(item => item.value)
}
function unwrapArray(items) {
return items.map(item => item.value)
}
const items = [{ value: 4 },{ value: 'foo' }]
const [num,str] = unwrap(...items)
console.log(num.toFixed(2))
console.log(str.charat(0))
这是一个带有函数的游乐场,以及每个函数的“测试”。他们使用的是非可变类型,这当然不是很有效,而且我不明白如何编写它们以便它们做工作:
TypeScript Playground
解决方法
以下是我倾向于输入它们的方式:
type Wrapped<T extends any[]> = { [I in keyof T]: { value: T[I] } };
function wrap<T extends any[]>(...items: T) {
return items.map(item => ({ value: item })) as Wrapped<T>;
}
function unwrap<T extends any[]>(...items: Wrapped<T>) {
return items.map(item => item.value) as T;
}
function wrapArray<T extends any[]>(items: readonly [...T]) {
return items.map(item => ({ value: item })) as Wrapped<T>
}
function unwrapArray<T extends any[]>(items: readonly [...Wrapped<T>]) {
return items.map(item => item.value) as T;
}
备注:
-
这些函数在
T
中都是通用的,即非包装元素的 tuple。所以wrap()
和wrapArray()
将T
类型的值作为输入,而unwrap()
和unwrapArray()
返回T
类型的值作为输出。 Wrapped<T>
是一个 mapped tuple,它包装了T
的每个元素。wrap()
和wrapArray()
返回Wrapped<T>
类型的值作为输出,而unwrap()
和unwrapArray()
将Wrapped<T>
类型的值作为输入。-
编译器本身无法验证或理解
items.map(...)
会将T
变成Wrapped<T>
,反之亦然。有关原因的详细信息,请参阅 this question/answer。我们没有试图让编译器这样做,而是简单地 assertmap()
的返回值是我们想要的类型(即,as T
或as Wrapped<T>
). -
wrap()
或unwrap()
中没有任何地方使用 variadic tuple types。这种元组类型可以通过使用元组内的数组或元组类型的其余参数来识别,例如[1,...[2,3,4],5]
。wrapArray()
和unwrapArray()
函数使用可变元组类型作为输入(readonly [...T]
而不仅仅是T
),但这只是为了在使用它们时帮助推断元组,而不是必要的。因此,当您询问可变参数元组类型时,该示例不需要使用它们。
让我们试一试:
const items = [{ value: 4 },{ value: 'foo' }] as const; // <-- note well
const [num,str] = unwrap(...items);
// or const [num,str] = unwrapArray(items);
console.log(num.toLocaleString());
console.log(str.charAt(0));
这现在可以工作了,但请注意,我必须使用 const
assertion 来让编译器推断 items
是正好有两个元素的元组,第一个是 {value: 4}
第二个是 {value: "foo"}
。没有它,编译器使用它的标准启发式假设一个数组是可变的,它的长度和它的元素的身份可以改变......这意味着 items
类似于 Array<{value: string} | {value: number}>
并且没有希望将其解包成一个元组。