编辑:此功能作为 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 正在正确解析类型,但打字稿编译器拒绝接受它。我想知道是否有另一种方法,或者是否有办法使我的递归工作。
答案 0 :(得分: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: {}; } */