Avoid infinite recursion when instantiating circular inline mapped generic tuple type (#53522)

This commit is contained in:
Mateusz Burzyński 2023-05-24 20:41:04 +02:00 committed by GitHub
parent fefcb81d48
commit 7baf6cd120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 0 deletions

View File

@ -18926,6 +18926,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const singleton = elementFlags[i] & ElementFlags.Variadic ? t :
elementFlags[i] & ElementFlags.Rest ? createArrayType(t) :
createTupleType([t], [elementFlags[i]]);
// avoid infinite recursion, if the singleton is the type variable itself
// then we'd just get back here with the same arguments from within instantiateMappedType
if (singleton === typeVariable) {
return mappedType;
}
// The singleton is never a generic tuple type, so it is safe to recurse here.
return instantiateMappedType(mappedType, prependTypeMapping(typeVariable, singleton, mapper));
});

View File

@ -0,0 +1,45 @@
=== tests/cases/compiler/circularInlineMappedGenericTupleTypeNoCrash.ts ===
class Foo<Elements extends readonly unknown[]> {
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 31))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 54))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 1, 31))
public constructor(
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
>elements : Symbol(elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 3, 21))
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 20))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 43))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
>P : Symbol(P, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 4, 20))
) {
this.elements = elements;
>this.elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
>this : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
>elements : Symbol(elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 3, 21))
}
public add(): Foo<[...Elements, "abc"]> {
>add : Symbol(Foo.add, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 7, 3))
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
>Foo : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
>Elements : Symbol(Elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 10))
>this.elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
>this : Symbol(Foo, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 0))
>elements : Symbol(Foo.elements, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 0, 48))
>bar : Symbol(bar, Decl(circularInlineMappedGenericTupleTypeNoCrash.ts, 10, 60))
}
}

View File

@ -0,0 +1,38 @@
=== tests/cases/compiler/circularInlineMappedGenericTupleTypeNoCrash.ts ===
class Foo<Elements extends readonly unknown[]> {
>Foo : Foo<Elements>
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>bar : Elements[P]
public constructor(
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>bar : Elements[P]
) {
this.elements = elements;
>this.elements = elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>this.elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>this : this
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
}
public add(): Foo<[...Elements, "abc"]> {
>add : () => Foo<[...Elements, "abc"]>
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
>new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" }) : Foo<[...Elements, "abc"]>
>Foo : typeof Foo
>...this.elements : { bar: unknown; }
>this.elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>this : this
>elements : { [P in keyof Elements]: { bar: Elements[P]; }; }
>{ bar: "abc" } : { bar: "abc"; }
>bar : "abc"
>"abc" : "abc"
}
}

View File

@ -0,0 +1,16 @@
// @strict: true
// @noEmit: true
class Foo<Elements extends readonly unknown[]> {
public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } };
public constructor(
...elements: { [P in keyof Elements]: { bar: Elements[P] } }
) {
this.elements = elements;
}
public add(): Foo<[...Elements, "abc"]> {
return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" });
}
}