在 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&lt;T&gt;,其中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&lt;T&gt; 类型。

您可以验证它与上述示例的行为相同:

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 中遍历可变参数元组的主要内容,如果未能解决你的问题,请参考以下文章

简明Python 可变参数

Python中 可变参数

可变长参数

DAY12

函数基础之调用,参数,可变长参数

python可变长参数