更新:TypeScript 4.0 将具有可变元组类型 https://github.com/microsoft/TypeScript/pull/39094,这将允许更灵活的内置元组操作。Push<T, V>
将简单地实现为[...T, V]
.
TS4.0 之前的答案:
呃,为什么?!咳咳,我的意思是,我也许可以做到这一点,但是涉及的类型杂耍越多,我就越不建议为任何重要的事情这样做。有一个图书馆叫ts-工具带 https://pirix-gh.github.io/ts-toolbelt/这接近于“官方支持” https://github.com/microsoft/TypeScript/pull/33810通过 TypeScript (尽管它不适用于操场 https://www.typescriptlang.org/play/, 至少not yet https://github.com/microsoft/TypeScript-Website/issues/92,所以我不打算做一个需要它的 Stack Overflow 答案),你可以在其中构建一些可行的东西。
我的处理方法是将带有可选元素的元组转换为不带有可选元素的元组的并集。不幸的是,我缺少一种内置的方法来获取数字类型,例如6
并获得该长度的元组。因此,我正在制作一个可以映射的各种长度元组的硬编码列表。如果您需要它来处理更长的元组,您可以扩展它。开始了:
type Cons<H, T extends readonly any[]> =
((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;
这只是标准Cons<1, [2,3,4]>
变成[1,2,3,4]
.
type Tup = [[], [0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; // make as long as you need
这是元组的大列表。所以Tup[4]
is [0,0,0,0]
, etc.
type TruncateTuple<T extends readonly any[], N extends number> = Extract<
Tup[N] extends infer R ? { [K in keyof R]: K extends keyof T ? T[K] : never }
: never, readonly any[]>;
该类型需要一个元组T
和一个长度N
并截断T
到长度N
. So TruncateTuple<[1,2,3,4], 2>
应该[1,2]
。它的工作原理是获取一个长度元组N
from Tup
,并用来自的属性映射它T
.
type OptTupleToUnion<T extends readonly any[]> =
TruncateTuple<Required<T>, T['length']>;
这是主要事件...OptTupleToUnion
需要一个元组T
并从中生成非可选元组的并集。它的工作原理是截断Required<T>
(那是,T
可选元素变成必需元素)到长度T['length']
这是可能长度的并集T
. So OptTupleToUnion<[1,2,3?,4?]>
应该成为[1,2] | [1,2,3] | [1,2,3,4]
.
然后我会重命名我的旧的Push
不妨碍_Push
:
type _Push<T extends readonly any[], V>
= T extends any ? Cons<void, T> extends infer U ?
{ [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;
和做Push<T, V>
act on OptTupleToUnion<T>
代替T
:
type Push<T extends readonly any[], V> = T extends any ?
_Push<OptTupleToUnion<T>, V> : never;
(与相同的T extends any ? ..T.. : never
以确保工会得到分配)
让我们看看它是否有效:
type A = Push<[1, 2, 3], 4>; // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3, 4] | [1, 2, 4]
是的,看起来不错。 ????如果你开始在这里要求更多功能,我可能不得不放弃......也许其他人有更多的耐力!
好的,希望有帮助;祝你好运!