Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b9eeb74

Browse filesBrowse files
authored
Tail recursive evaluation of conditional types (microsoft#45711)
* Implement tail-recursion for conditional types and lower general instantiation depth limit to 100 * Add tests * Skip caching for tail recursive temporary conditional types
1 parent 4f5bbd0 commit b9eeb74
Copy full SHA for b9eeb74

5 files changed

+296-4Lines changed: 296 additions & 4 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+46-4Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15265,10 +15265,18 @@ namespace ts {
1526515265
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
1526615266
let result;
1526715267
let extraTypes: Type[] | undefined;
15268+
let tailCount = 0;
1526815269
// We loop here for an immediately nested conditional type in the false position, effectively treating
1526915270
// types of the form 'A extends B ? X : C extends D ? Y : E extends F ? Z : ...' as a single construct for
15270-
// purposes of resolution. This means such types aren't subject to the instantiation depth limiter.
15271+
// purposes of resolution. We also loop here when resolution of a conditional type ends in resolution of
15272+
// another (or, through recursion, possibly the same) conditional type. In the potentially tail-recursive
15273+
// cases we increment the tail recursion counter and stop after 1000 iterations.
1527115274
while (true) {
15275+
if (tailCount === 1000) {
15276+
error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite);
15277+
result = errorType;
15278+
break;
15279+
}
1527215280
const isUnwrapped = isTypicalNondistributiveConditional(root);
1527315281
const checkType = instantiateType(unwrapNondistributiveConditionalTuple(root, getActualTypeVariable(root.checkType)), mapper);
1527415282
const checkTypeInstantiable = isGenericType(checkType);
@@ -15312,6 +15320,9 @@ namespace ts {
1531215320
root = newRoot;
1531315321
continue;
1531415322
}
15323+
if (canTailRecurse(falseType, mapper)) {
15324+
continue;
15325+
}
1531515326
}
1531615327
result = instantiateType(falseType, mapper);
1531715328
break;
@@ -15322,7 +15333,12 @@ namespace ts {
1532215333
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
1532315334
// doesn't immediately resolve to 'string' instead of being deferred.
1532415335
if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) {
15325-
result = instantiateType(getTypeFromTypeNode(root.node.trueType), combinedMapper || mapper);
15336+
const trueType = getTypeFromTypeNode(root.node.trueType);
15337+
const trueMapper = combinedMapper || mapper;
15338+
if (canTailRecurse(trueType, trueMapper)) {
15339+
continue;
15340+
}
15341+
result = instantiateType(trueType, trueMapper);
1532615342
break;
1532715343
}
1532815344
}
@@ -15338,6 +15354,32 @@ namespace ts {
1533815354
break;
1533915355
}
1534015356
return extraTypes ? getUnionType(append(extraTypes, result)) : result;
15357+
// We tail-recurse for generic conditional types that (a) have not already been evaluated and cached, and
15358+
// (b) are non distributive, have a check type that is unaffected by instantiation, or have a non-union check
15359+
// type. Note that recursion is possible only through aliased conditional types, so we only increment the tail
15360+
// recursion counter for those.
15361+
function canTailRecurse(newType: Type, newMapper: TypeMapper | undefined) {
15362+
if (newType.flags & TypeFlags.Conditional && newMapper) {
15363+
const newRoot = (newType as ConditionalType).root;
15364+
if (newRoot.outerTypeParameters) {
15365+
const typeParamMapper = combineTypeMappers((newType as ConditionalType).mapper, newMapper);
15366+
const typeArguments = map(newRoot.outerTypeParameters, t => getMappedType(t, typeParamMapper));
15367+
const newRootMapper = createTypeMapper(newRoot.outerTypeParameters, typeArguments);
15368+
const newCheckType = newRoot.isDistributive ? getMappedType(newRoot.checkType, newRootMapper) : undefined;
15369+
if (!newCheckType || newCheckType === newRoot.checkType || !(newCheckType.flags & (TypeFlags.Union | TypeFlags.Never))) {
15370+
root = newRoot;
15371+
mapper = newRootMapper;
15372+
aliasSymbol = undefined;
15373+
aliasTypeArguments = undefined;
15374+
if (newRoot.aliasSymbol) {
15375+
tailCount++;
15376+
}
15377+
return true;
15378+
}
15379+
}
15380+
}
15381+
return false;
15382+
}
1534115383
}
1534215384

1534315385
function getTrueTypeFromConditionalType(type: ConditionalType) {
@@ -16358,8 +16400,8 @@ namespace ts {
1635816400
if (!couldContainTypeVariables(type)) {
1635916401
return type;
1636016402
}
16361-
if (instantiationDepth === 500 || instantiationCount >= 5000000) {
16362-
// We have reached 500 recursive type instantiations, or 5M type instantiations caused by the same statement
16403+
if (instantiationDepth === 100 || instantiationCount >= 5000000) {
16404+
// We have reached 100 recursive type instantiations, or 5M type instantiations caused by the same statement
1636316405
// or expression. There is a very high likelyhood we're dealing with a combination of infinite generic types
1636416406
// that perpetually generate new type identities, so we stop the recursion here by yielding the error type.
1636516407
tracing?.instant(tracing.Phase.CheckTypes, "instantiateType_DepthLimit", { typeId: type.id, instantiationDepth, instantiationCount });
Collapse file
+49Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [tailRecursiveConditionalTypes.ts]
2+
type Trim<S extends string> =
3+
S extends ` ${infer T}` ? Trim<T> :
4+
S extends `${infer T} ` ? Trim<T> :
5+
S;
6+
7+
type T10 = Trim<' hello '>;
8+
type T11 = Trim<' hello '>;
9+
10+
type GetChars<S> = GetCharsRec<S, never>;
11+
type GetCharsRec<S, Acc> =
12+
S extends `${infer Char}${infer Rest}` ? GetCharsRec<Rest, Char | Acc> : Acc;
13+
14+
type T20 = GetChars<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>;
15+
16+
type Reverse<T> = any[] extends T ? T : ReverseRec<T, []>;
17+
type ReverseRec<T, Acc extends unknown[]> =
18+
T extends [infer Head, ...infer Tail] ? ReverseRec<Tail, [Head, ...Acc]> : Acc;
19+
20+
type T30 = Reverse<[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
21+
type T31 = Reverse<string[]>;
22+
23+
type TupleOf<T, N extends number> = number extends N ? T[] : TupleOfRec<T, N, []>;
24+
type TupleOfRec<T, N extends number, Acc extends unknown[]> =
25+
Acc["length"] extends N ? Acc : TupleOfRec<T, N, [T, ...Acc]>;
26+
27+
type T40 = TupleOf<any, 200>;
28+
type T41 = TupleOf<any, number>;
29+
30+
31+
//// [tailRecursiveConditionalTypes.js]
32+
"use strict";
33+
34+
35+
//// [tailRecursiveConditionalTypes.d.ts]
36+
declare type Trim<S extends string> = S extends ` ${infer T}` ? Trim<T> : S extends `${infer T} ` ? Trim<T> : S;
37+
declare type T10 = Trim<' hello '>;
38+
declare type T11 = Trim<' hello '>;
39+
declare type GetChars<S> = GetCharsRec<S, never>;
40+
declare type GetCharsRec<S, Acc> = S extends `${infer Char}${infer Rest}` ? GetCharsRec<Rest, Char | Acc> : Acc;
41+
declare type T20 = GetChars<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>;
42+
declare type Reverse<T> = any[] extends T ? T : ReverseRec<T, []>;
43+
declare type ReverseRec<T, Acc extends unknown[]> = T extends [infer Head, ...infer Tail] ? ReverseRec<Tail, [Head, ...Acc]> : Acc;
44+
declare type T30 = Reverse<[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
45+
declare type T31 = Reverse<string[]>;
46+
declare type TupleOf<T, N extends number> = number extends N ? T[] : TupleOfRec<T, N, []>;
47+
declare type TupleOfRec<T, N extends number, Acc extends unknown[]> = Acc["length"] extends N ? Acc : TupleOfRec<T, N, [T, ...Acc]>;
48+
declare type T40 = TupleOf<any, 200>;
49+
declare type T41 = TupleOf<any, number>;
Collapse file
+118Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
=== tests/cases/compiler/tailRecursiveConditionalTypes.ts ===
2+
type Trim<S extends string> =
3+
>Trim : Symbol(Trim, Decl(tailRecursiveConditionalTypes.ts, 0, 0))
4+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 0, 10))
5+
6+
S extends ` ${infer T}` ? Trim<T> :
7+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 0, 10))
8+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 1, 23))
9+
>Trim : Symbol(Trim, Decl(tailRecursiveConditionalTypes.ts, 0, 0))
10+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 1, 23))
11+
12+
S extends `${infer T} ` ? Trim<T> :
13+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 0, 10))
14+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 2, 22))
15+
>Trim : Symbol(Trim, Decl(tailRecursiveConditionalTypes.ts, 0, 0))
16+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 2, 22))
17+
18+
S;
19+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 0, 10))
20+
21+
type T10 = Trim<' hello '>;
22+
>T10 : Symbol(T10, Decl(tailRecursiveConditionalTypes.ts, 3, 6))
23+
>Trim : Symbol(Trim, Decl(tailRecursiveConditionalTypes.ts, 0, 0))
24+
25+
type T11 = Trim<' hello '>;
26+
>T11 : Symbol(T11, Decl(tailRecursiveConditionalTypes.ts, 5, 170))
27+
>Trim : Symbol(Trim, Decl(tailRecursiveConditionalTypes.ts, 0, 0))
28+
29+
type GetChars<S> = GetCharsRec<S, never>;
30+
>GetChars : Symbol(GetChars, Decl(tailRecursiveConditionalTypes.ts, 6, 170))
31+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 8, 14))
32+
>GetCharsRec : Symbol(GetCharsRec, Decl(tailRecursiveConditionalTypes.ts, 8, 41))
33+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 8, 14))
34+
35+
type GetCharsRec<S, Acc> =
36+
>GetCharsRec : Symbol(GetCharsRec, Decl(tailRecursiveConditionalTypes.ts, 8, 41))
37+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 9, 17))
38+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 9, 19))
39+
40+
S extends `${infer Char}${infer Rest}` ? GetCharsRec<Rest, Char | Acc> : Acc;
41+
>S : Symbol(S, Decl(tailRecursiveConditionalTypes.ts, 9, 17))
42+
>Char : Symbol(Char, Decl(tailRecursiveConditionalTypes.ts, 10, 22))
43+
>Rest : Symbol(Rest, Decl(tailRecursiveConditionalTypes.ts, 10, 35))
44+
>GetCharsRec : Symbol(GetCharsRec, Decl(tailRecursiveConditionalTypes.ts, 8, 41))
45+
>Rest : Symbol(Rest, Decl(tailRecursiveConditionalTypes.ts, 10, 35))
46+
>Char : Symbol(Char, Decl(tailRecursiveConditionalTypes.ts, 10, 22))
47+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 9, 19))
48+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 9, 19))
49+
50+
type T20 = GetChars<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>;
51+
>T20 : Symbol(T20, Decl(tailRecursiveConditionalTypes.ts, 10, 81))
52+
>GetChars : Symbol(GetChars, Decl(tailRecursiveConditionalTypes.ts, 6, 170))
53+
54+
type Reverse<T> = any[] extends T ? T : ReverseRec<T, []>;
55+
>Reverse : Symbol(Reverse, Decl(tailRecursiveConditionalTypes.ts, 12, 86))
56+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 14, 13))
57+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 14, 13))
58+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 14, 13))
59+
>ReverseRec : Symbol(ReverseRec, Decl(tailRecursiveConditionalTypes.ts, 14, 58))
60+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 14, 13))
61+
62+
type ReverseRec<T, Acc extends unknown[]> =
63+
>ReverseRec : Symbol(ReverseRec, Decl(tailRecursiveConditionalTypes.ts, 14, 58))
64+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 15, 16))
65+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 15, 18))
66+
67+
T extends [infer Head, ...infer Tail] ? ReverseRec<Tail, [Head, ...Acc]> : Acc;
68+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 15, 16))
69+
>Head : Symbol(Head, Decl(tailRecursiveConditionalTypes.ts, 16, 20))
70+
>Tail : Symbol(Tail, Decl(tailRecursiveConditionalTypes.ts, 16, 35))
71+
>ReverseRec : Symbol(ReverseRec, Decl(tailRecursiveConditionalTypes.ts, 14, 58))
72+
>Tail : Symbol(Tail, Decl(tailRecursiveConditionalTypes.ts, 16, 35))
73+
>Head : Symbol(Head, Decl(tailRecursiveConditionalTypes.ts, 16, 20))
74+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 15, 18))
75+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 15, 18))
76+
77+
type T30 = Reverse<[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
78+
>T30 : Symbol(T30, Decl(tailRecursiveConditionalTypes.ts, 16, 83))
79+
>Reverse : Symbol(Reverse, Decl(tailRecursiveConditionalTypes.ts, 12, 86))
80+
81+
type T31 = Reverse<string[]>;
82+
>T31 : Symbol(T31, Decl(tailRecursiveConditionalTypes.ts, 18, 171))
83+
>Reverse : Symbol(Reverse, Decl(tailRecursiveConditionalTypes.ts, 12, 86))
84+
85+
type TupleOf<T, N extends number> = number extends N ? T[] : TupleOfRec<T, N, []>;
86+
>TupleOf : Symbol(TupleOf, Decl(tailRecursiveConditionalTypes.ts, 19, 29))
87+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 21, 13))
88+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 21, 15))
89+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 21, 15))
90+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 21, 13))
91+
>TupleOfRec : Symbol(TupleOfRec, Decl(tailRecursiveConditionalTypes.ts, 21, 82))
92+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 21, 13))
93+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 21, 15))
94+
95+
type TupleOfRec<T, N extends number, Acc extends unknown[]> =
96+
>TupleOfRec : Symbol(TupleOfRec, Decl(tailRecursiveConditionalTypes.ts, 21, 82))
97+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 22, 16))
98+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 22, 18))
99+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 22, 36))
100+
101+
Acc["length"] extends N ? Acc : TupleOfRec<T, N, [T, ...Acc]>;
102+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 22, 36))
103+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 22, 18))
104+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 22, 36))
105+
>TupleOfRec : Symbol(TupleOfRec, Decl(tailRecursiveConditionalTypes.ts, 21, 82))
106+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 22, 16))
107+
>N : Symbol(N, Decl(tailRecursiveConditionalTypes.ts, 22, 18))
108+
>T : Symbol(T, Decl(tailRecursiveConditionalTypes.ts, 22, 16))
109+
>Acc : Symbol(Acc, Decl(tailRecursiveConditionalTypes.ts, 22, 36))
110+
111+
type T40 = TupleOf<any, 200>;
112+
>T40 : Symbol(T40, Decl(tailRecursiveConditionalTypes.ts, 23, 66))
113+
>TupleOf : Symbol(TupleOf, Decl(tailRecursiveConditionalTypes.ts, 19, 29))
114+
115+
type T41 = TupleOf<any, number>;
116+
>T41 : Symbol(T41, Decl(tailRecursiveConditionalTypes.ts, 25, 29))
117+
>TupleOf : Symbol(TupleOf, Decl(tailRecursiveConditionalTypes.ts, 19, 29))
118+
Collapse file
+53Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
=== tests/cases/compiler/tailRecursiveConditionalTypes.ts ===
2+
type Trim<S extends string> =
3+
>Trim : Trim<S>
4+
5+
S extends ` ${infer T}` ? Trim<T> :
6+
S extends `${infer T} ` ? Trim<T> :
7+
S;
8+
9+
type T10 = Trim<' hello '>;
10+
>T10 : "hello"
11+
12+
type T11 = Trim<' hello '>;
13+
>T11 : "hello"
14+
15+
type GetChars<S> = GetCharsRec<S, never>;
16+
>GetChars : GetChars<S>
17+
18+
type GetCharsRec<S, Acc> =
19+
>GetCharsRec : GetCharsRec<S, Acc>
20+
21+
S extends `${infer Char}${infer Rest}` ? GetCharsRec<Rest, Char | Acc> : Acc;
22+
23+
type T20 = GetChars<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>;
24+
>T20 : "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
25+
26+
type Reverse<T> = any[] extends T ? T : ReverseRec<T, []>;
27+
>Reverse : Reverse<T>
28+
29+
type ReverseRec<T, Acc extends unknown[]> =
30+
>ReverseRec : ReverseRec<T, Acc>
31+
32+
T extends [infer Head, ...infer Tail] ? ReverseRec<Tail, [Head, ...Acc]> : Acc;
33+
34+
type T30 = Reverse<[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
35+
>T30 : [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
36+
37+
type T31 = Reverse<string[]>;
38+
>T31 : string[]
39+
40+
type TupleOf<T, N extends number> = number extends N ? T[] : TupleOfRec<T, N, []>;
41+
>TupleOf : TupleOf<T, N>
42+
43+
type TupleOfRec<T, N extends number, Acc extends unknown[]> =
44+
>TupleOfRec : TupleOfRec<T, N, Acc>
45+
46+
Acc["length"] extends N ? Acc : TupleOfRec<T, N, [T, ...Acc]>;
47+
48+
type T40 = TupleOf<any, 200>;
49+
>T40 : [any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any]
50+
51+
type T41 = TupleOf<any, number>;
52+
>T41 : any[]
53+

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.