gpt4 book ai didi

typescript - Typescript:如何实现这种类型的递归?

转载 作者:搜寻专家 更新时间:2023-10-30 21:09:51 28 4
gpt4 key购买 nike

我最初的问题是this
我做了以下类型,但由于循环引用错误而无法工作,我不知道如何解决:

type Increment<T extends number, Tuple extends any[] = [any]> = 
T extends 0 ? 1 :
T extends 1 ? 2 :
T extends TupleUnshift<any, Tuple> ?
TupleUnshift<any, TupleUnshift<any, Tuple>>['length'] :
Increment<T, TupleUnshift<any, TupleUnshift<any, Tuple>>>

最后,它应该像:
type five = 5
type six = Increment<five> // 6

p.s. TupleUnshift来自 here

最佳答案

我认为在另一个问题和注释中,我说过您可以尝试递归地这样做,但是编译器,即使您可以使它满意于定义,也会在相对较浅的深度上放弃。让我们看看这里:

interface Reduction<Base, In> {
0: Base
1: In
}
type Reduce<T, R extends Reduction<any, any>, Base =[]> =
R[[T] extends [Base] ? 0 : 1]

type Cons<H, T extends any[]> = ((h: H, ...t: T) => void) extends
((...c: infer C) => void) ? C : never;

interface TupleOfIncrementedLength<N extends number, Tuple extends any[]=[]>
extends Reduction<Tuple, Reduce<Tuple['length'],
TupleOfIncrementedLength<N, Cons<any, Tuple>>, N
>> { }

type Increment<N extends number> = TupleOfIncrementedLength<N>[1] extends
{ length: infer M } ? M : never;

编辑:你要求解释,所以这里是:
首先, Cons<H, T>的定义使用 tuples in rest/spread positionsconditional types获取头类型 H和元组尾类型 T,并返回一个新元组,其中头部已前置到尾部。(名称AA>来自LISP中的列表操作,后来出现在Haskell中),因此 Cons<"a", ["b","c"]>评估为 ["a","b","c"]
作为背景,TypeScript通常试图阻止你做循环类型。你可以通过使用一些 "cons"来延迟一些执行,比如:
type RecursiveThing<T> = 
{ 0: BaseCase, 1: RecursiveThing<...T...> }[Test<...T...> extends BaseCaseTest ? 0 : 1]

这应该对 BaseCaseRecursiveThing<...T...>进行评估,但是索引访问中的条件类型会被延迟,因此编译器没有意识到它最终会成为循环引用。不幸的是,这有一个非常糟糕的副作用,即导致编译器在无限类型上彻底崩溃,并且当您开始在实践中使用这些东西时常常陷入困境。例如,您可以这样定义 TupleOfIncrementedLength<>
type TupleOfIncrementedLength<N extends number, Tuple extends any[]=[]> = {
0: Cons<any, Tuple>,
1: TupleOfIncrementedLength<N, Cons<any, Tuple>>
}[Tuple['length'] extends N ? 0 : 1]

它可以工作,甚至可以工作到40或50左右。但是如果你把它放在一个库中并开始使用它,你可能会发现编译时间变得非常高,甚至会导致编译器崩溃。这是不一致的,所以我不能很容易地在这里产生一个例子,但它咬我足够我避免它。这个问题有可能最终得到解决。但现在,我将遵循 conditional typesadvice(typescript的首席架构师):
它很聪明,但它绝对会把东西推得远远超出预期用途。虽然它可能适用于小的例子,但它的规模会非常大。解决这些深度递归类型消耗大量的时间和资源,并且将来可能会与我们在检查器中的递归管理器相冲突。
别这样!
输入 @ahejlsberg,WHO AA>,因为 N声明不被急切地评估,即 interface声明是(因为 type s只是别名,编译器试图对它们进行评估),如果可以以正确的方式扩展 type,结合上面的条件类型技巧,编译器就不应该陷入困境。我的实验证实了这一点,但我仍然不相信…可能有一个反例,只有当你试图组成这些东西时才会出现。
不管怎样,上述 interface类型的 TupleOfIncrementedLengthReduction与以前的“navy”版本的工作方式大致相同,只是它是 Reduce。我真的不能不写十页就把它分开,我不想这样做,对不起。实际输出是一个 interface,其 Reduction属性具有我们关心的元组。
之后,通过获取 1属性并提取其 Increment,根据 TupleOfIncrementedLength定义 1(我不使用普通索引访问,因为编译器无法推断 length是数组类型。幸运的是条件类型推理拯救了我们)。
我不知道这样做对我来说有多大用处。只要说它不断增加元组的长度,直到它的长度大于 TupleOfIncrementedLength<N>[1]参数,然后返回元组的长度。它确实有效,对于small N
type Seven = Increment<6>; // 7
type Sixteen = Increment<15>; // 16

但是,至少在我的编译器(版本3.3.0-dev.20190117)上,会发生以下情况:
type TwentyThree = Increment<22>; // 23
type TwentyWhaaaa = Increment<23>; // {} 😞
type Whaaaaaaaaaa = Increment<100>; // {} 🙁

而且,你对工会也有一些奇怪的看法,而且:
type WhatDoYouThink = Increment<5 | 10>; // 6 😕
type AndThis = Increment<number>; // 1 😕

你可能会将这两种行为修复为 @strax,但这感觉就像是在拯救一艘正在下沉的船。
如果上述超级复杂和脆弱的方法完全在23给出,那么你也可以使用一个硬编码的输出列表,正如我在 discovered中所展示的。对于任何想在一个地方看到答案的人来说,这是:
type Increment<N extends number> = [
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,
38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54, // as far as you need
...number[] // bail out with number
][N]

这比较简单,适用于更高的数字:
type Seven = Increment<6>; // 7
type Sixteen = Increment<15>; // 16
type TwentyThree = Increment<22>; // 23
type TwentyFour = Increment<23>; // 24
type FiftyFour = Increment<53>; // 54

通常失败时更优雅,而在已知的地方失败:
type NotFiftyFiveButNumber = Increment<54>; // number
type NotOneHundredOneButNumber = Increment<100>; // number

在“奇怪”的情况下,自然会有更好的事情发生。
type WhatDoYouThink = Increment<5 | 10>; // 6 | 11 👍
type AndThis = Increment<number>; // number 👍

总之,在我看来,递归类型比它们在这里的价值更麻烦。
好吧,希望能再帮上忙。祝你好运!

关于typescript - Typescript:如何实现这种类型的递归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54243565/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com