TS4.1的更新+
欢迎回来!引入TypeScript 4.1
recursive conditional types
,以及
variadic tuple types
使通过递归执行“将一添加到非负整数”成为可能。它实际上表现得很好,但是
我仍然不建议在生产环境中使用它
原因我很快就会谈到。
首先,我会的
无耻地抄袭
借鉴
this comment
通过
@lazytype
在microsoft/TypeScript#26223中,它生成任意非负整数长度的元组,而不会在深度23处打破递归限制。它通过将整数分解为两个不同幂的和(即,使用其二进制表示)并连接这些长度的元组来实现这一点。可变元组类型使元组的长度加倍变得非常容易(
[...T, ...T]
type BuildPowersOf2LengthArrays<N extends number, R extends never[][]> =
R[0][N] extends never ? R : BuildPowersOf2LengthArrays<N, [[...R[0], ...R[0]], ...R]>;
type ConcatLargestUntilDone<N extends number, R extends never[][], B extends never[]> =
B["length"] extends N ? B : [...R[0], ...B][N] extends never
? ConcatLargestUntilDone<N, R extends [R[0], ...infer U] ? U extends never[][] ? U : never : never, B>
: ConcatLargestUntilDone<N, R extends [R[0], ...infer U] ? U extends never[][] ? U : never : never, [...R[0], ...B]>;
type Replace<R extends any[], T> = { [K in keyof R]: T }
type TupleOf<T, N extends number> = number extends N ? T[] : {
[K in N]:
BuildPowersOf2LengthArrays<K, [[never]]> extends infer U ? U extends never[][]
? Replace<ConcatLargestUntilDone<K, U, []>, T> : never : never;
}[N]
然后
Increment
您想要的类型如下所示:
type Increment<N extends number> = [0, ...TupleOf<0, N>]['length'];
这将创建一个长度为的元组
N
用零填充(不管你在那里使用什么),在一个
0
然后得到它的长度。
让我们看看它的实际行动:
type Seven = Increment<6>; // 7
type Sixteen = Increment<15>; // 16
type OneHundredOne = Increment<100>; // 101
type OneThousand = Increment<999>; // 1000
type SixOrElevent = Increment<5 | 10>; // 6 | 11
type Numbah = Increment<number>; // number
美好的我想这正是我们想要的。现在谈谈不好的部分以及我不推荐它的原因:
// Don't do this
type Kablooey = Increment<3.14> // Loading... ð¥ð¥ð»ð¥ð¥
这将导致编译器心脏病发作;递归元组生成器永远达不到它的目标,因为不管它生成的元组有多大,索引中都没有元素
3.14
TupleOf
如果数字的字符串表示形式中有小数点,请键入以跳出(呼出到
template literal types
!):
type TupleOf<T, N extends number> = number extends N ? T[] :
`${N}` extends `${infer X}.${infer Y}` ? T[] : {
[K in N]:
BuildPowersOf2LengthArrays<K, [[never]]> extends infer U ? U extends never[][]
? Replace<ConcatLargestUntilDone<K, U, []>, T> : never : never;
}[N]
现在
3.14
导致
number
,但事实并非如此
4.14
,但至少是合理的,且不过分:
type AlsoNumber = Increment<3.14> // number
不过,我仍然无法告诉任何人“请在您的生产环境中使用它”。它太脆弱了。看看上面的拜占庭类型munging,您有多确定没有一些简单的边缘情况会使编译器崩溃?您是否想负责修补这些东西,只是为了让编译器将一个添加到一个数字中?
相反,我仍然建议进行简单的查找,直到和除非TypeScript按照中的要求对数字文本执行算术
microsoft/TypeScript#15645
和/或
microsoft/TypeScript#26382
.
Playground link to code
早期TS版本的先前答案:
我认为在另一个问题和评论中,我说你可以
尝试
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 positions
和
conditional types
头型
H
和一个元组尾类型
T
并返回一个新的元组,其中头部已前置到尾部。(姓名
"cons"
源于Lisp中的列表操作和后来的Haskell。)所以
Cons<"a", ["b","c"]>
评估为
["a","b","c"]
作为背景,TypeScript通常试图阻止您执行循环类型。你可以使用一些工具在后门溜达
条件类型
要推迟执行,请执行以下操作:
type RecursiveThing<T> =
{ 0: BaseCase, 1: RecursiveThing<...T...> }[Test<...T...> extends BaseCaseTest ? 0 : 1]
这应该评估为
BaseCase
或
RecursiveThing<...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]
而且它也能起作用,即使对你来说也是如此
N
最多40或50左右。但是,如果您将它放在库中并开始使用它,您可能会发现编译时间变得非常长,甚至导致编译器崩溃。这是不一致的,所以我不能很容易地在这里生成一个例子,但它咬我足够让我避免它。这个问题有可能最终得到解决。但现在,我会遵循
advice
属于
@ahejlsberg
(TypeScript的首席架构师):
它很聪明,但它肯定会使事物远远超出其预期用途。虽然它可能适用于小的例子,但它的规模会非常大。解决这些深度递归类型会消耗大量的时间和资源,并且将来可能会与我们在检查器中的递归调控器发生冲突。
进来
@strax
谁
discovered
那以后
interface
声明的求值方式与
type
声明是(自
类型
如果可以扩展
界面
以正确的方式,结合上面的条件类型技巧,编译器不应该陷入困境。我的实验证实了这一点,但我仍然不相信。。。可能会有一个反例,只有当你尝试编写这些东西时才会出现。
无论如何,上面
TupleOfIncrementedLength
Reduction
和
Reduce
工作原理与以前的“中堂”版本基本相同,只是它是一个
. 我真的不能不写十页就把它拆开,我也不想写,对不起。实际输出为a
减少
1
属性具有我们关心的元组。
之后,
定期的加薪
定义为
增加长度的元组
1.
属性及其提取
length
TupleOfIncrementedLength<N>[1]
是数组类型。幸运的是,条件类型推断节省了我们的时间)。
只要说它不断增加元组的长度,直到它的长度比
参数,然后返回该元组的长度。对于小公司来说,它确实有效
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 ð
您可能可以通过以下方法修复这两种行为:
conditional types
,但这感觉就像你在救一艘正在下沉的船。
the answer to the other question
. 对于那些在家里随波逐流、希望在一个地方看到答案的人来说,这是:
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 ð
好吧,希望这能再次帮助你。祝你好运