Skip to content

Commit 7baf6cd

Browse files
authored
Avoid infinite recursion when instantiating circular inline mapped generic tuple type (#53522)
1 parent fefcb81 commit 7baf6cd

File tree

4 files changed

+104
-0
lines changed

4 files changed

+104
-0
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18926,6 +18926,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1892618926
const singleton = elementFlags[i] & ElementFlags.Variadic ? t :
1892718927
elementFlags[i] & ElementFlags.Rest ? createArrayType(t) :
1892818928
createTupleType([t], [elementFlags[i]]);
18929+
// avoid infinite recursion, if the singleton is the type variable itself
18930+
// then we'd just get back here with the same arguments from within instantiateMappedType
18931+
if (singleton === typeVariable) {
18932+
return mappedType;
18933+
}
1892918934
// The singleton is never a generic tuple type, so it is safe to recurse here.
1893018935
return instantiateMappedType(mappedType, prependTypeMapping(typeVariable, singleton, mapper));
1893118936
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/circularInlineMappedGenericTupleTypeNoCrash.ts ===
2+
class Foo<Elements extends readonly unknown[]> {
3+
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
4+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
5+
6+
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
7+
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
8+
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 31))
9+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
10+
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 54))
11+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
12+
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 31))
13+
14+
public constructor(
15+
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
16+
>elements : Symbol(elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 3, 21))
17+
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 20))
18+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
19+
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 43))
20+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
21+
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 20))
22+
23+
) {
24+
this.elements = elements;
25+
>this.elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
26+
>this : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
27+
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
28+
>elements : Symbol(elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 3, 21))
29+
}
30+
31+
public add(): Foo<[...Elements, "abc"]> {
32+
>add : Symbol(Foo.add, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 7, 3))
33+
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
34+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
35+
36+
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
37+
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
38+
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
39+
>this.elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
40+
>this : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
41+
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
42+
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 10, 60))
43+
}
44+
}
45+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/compiler/circularInlineMappedGenericTupleTypeNoCrash.ts ===
2+
class Foo<Elements extends readonly unknown[]> {
3+
>Foo : Foo<Elements>
4+
5+
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
6+
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
7+
>bar : Elements[P]
8+
9+
public constructor(
10+
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
11+
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
12+
>bar : Elements[P]
13+
14+
) {
15+
this.elements = elements;
16+
>this.elements = elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
17+
>this.elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
18+
>this : this
19+
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
20+
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
21+
}
22+
23+
public add(): Foo<[...Elements, "abc"]> {
24+
>add : () => Foo<[...Elements, "abc"]>
25+
26+
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
27+
>new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" }) : Foo<[...Elements, "abc"]>
28+
>Foo : typeof Foo
29+
>...this.elements : { bar: unknown; }
30+
>this.elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
31+
>this : this
32+
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
33+
>{ bar: "abc" } : { bar: "abc"; }
34+
>bar : "abc"
35+
>"abc" : "abc"
36+
}
37+
}
38+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
class Foo<Elements extends readonly unknown[]> {
5+
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
6+
7+
public constructor(
8+
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
9+
) {
10+
this.elements = elements;
11+
}
12+
13+
public add(): Foo<[...Elements, "abc"]> {
14+
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
15+
}
16+
}

0 commit comments

Comments
 (0)