在 Typescript 中遍历可变参数元组
Posted
技术标签:
【中文标题】在 Typescript 中遍历可变参数元组【英文标题】:Traversing Variadic Tuple in Typescript 【发布时间】:2021-07-28 06:11:35 【问题描述】:编辑:这个功能是作为 TypeScript 4.1 的一部分提供的,就像 @jcalz 提到的那样。
我想做一个接受元组并遍历它的泛型类型。我的第一种方法是递归,但我得到了Type alias 'YourOperator' circularly references itself.
。这是我尝试过的最简单的例子
type VariadicAnd<T extends any[]> = T extends [infer Head, ...infer Tail] ? Head & VariadicAnd<Tail> : unknown
在我的具体情况下,我还想通过将Head
传递到另一个泛型类型中来对其进行转换。例如:
type SimpleTransform<T> = wrapped: T
type VariadicAndWithTransform<T extends any[]> = T extends [infer Head, ...infer Tail]
? SimpleTransform<Head> & VariadicAndWithTransform<Tail>
: unknown;
有趣的是,我的 IntelliSence 可以正确解析类型,但 typescript 编译器拒绝接受它。我想知道是否有另一种方法,或者是否有办法让我的递归工作。
【问题讨论】:
我无法重现您的错误,请参阅this Playground link。 TypeScript 从 4.1 版开始支持recursive conditional types;您使用的是旧版本的语言吗?如果是这样,您应该升级。如果不能,您应该说明您对 TS 版本号的要求。肯定有一种非递归的方法,但我喜欢有一个好的minimal reproducible example,所以我不会花太多时间去追查结果证明不是问题的东西。 @jcalz 感谢您的反馈!是的,你是对的,我将更新我的问题以指定这已在最新版本的打字稿中得到修复。感谢分享游乐场链接!了解我的问题非常有用。出于好奇,还有什么替代方案? 【参考方案1】:如上所述,在引入对 recursive conditional types 的支持之后,您的版本在 TypeScript 4.1 及更高版本中可以正常工作。
type VariadicAndWithTransform<T extends any[]> = T extends [infer F, ...infer R]
? SimpleTransform<F> & VariadicAndWithTransform<R>
: unknown; // no error
type Works = VariadicAndWithTransform<[ a: 1 , b: 2 , c: 3 ]>;
/* type Works = SimpleTransform<
a: 1;
> & SimpleTransform<
b: 2;
> & SimpleTransform<
c: 3;
> */
有一些变通方法可以诱使编译器允许 4.1 之前的类型,但它们并未得到官方支持。如果你需要递归条件类型,你应该升级你的 TypeScript 版本。
但对于此处所需的类型函数,您不需要递归类型。这实际上是一件好事,因为递归类型对编译器的负担更大,并且递归限制相当浅。如果您使用上述版本的VariadicAndWithTransform<T>
,其中T
有几十个元素,您会看到错误,即使在TS4.1+ 中也是如此:
type Three = [, , ];
type Nine = [...Three, ...Three, ...Three];
type TwentySeven = [...Nine, ...Nine, ...Nine]
type UhOh = VariadicAndWithTransform<TwentySeven>; // error!
// -------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite.
非递归版本对人类来说更难理解,但它让编译器更容易:
type VariadicAndWithTransform<T extends any[]> =
[K in keyof T]: (v: SimpleTransform<T[K]>) => void
[number] extends ((v: infer I) => void) ? I : never
它由位于逆变位置的联合中的inferring in a conditional type 工作(例如函数的参数),类似于this question 的答案中的UnionToIntersection<T>
类型。
您可以验证它与上述示例的行为相同:
type Works = VariadicAndWithTransform<[ a: 1 , b: 2 , c: 3 ]>;
/* type Works = SimpleTransform<
a: 1;
> & SimpleTransform<
b: 2;
> & SimpleTransform<
c: 3;
> */
而且因为它不使用递归,所以处理更长的元组也不会有问题:
type StillWorks = VariadicAndWithTransform<TwentySeven>;
/* type StillWorks = wrapped: ; */
Playground link to code
【讨论】:
哇,您的回答比我认为的要多。感谢分享!以上是关于在 Typescript 中遍历可变参数元组的主要内容,如果未能解决你的问题,请参考以下文章