diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0567712f11da3..9eb05af18a4e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25300,6 +25300,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getRecursionIdentity(type: Type): object { // Object and array literals are known not to contain recursive references and don't need a recursion identity. if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) { + if (isArrayType(type)) { + // Array wrappers are common and finite in non-recursive object graphs; track recursion identity through + // the element type instead of the shared global Array symbol to avoid false positives in deep nesting. + return getRecursionIdentity(getTypeArguments(type)[0]); + } if (getObjectFlags(type) & ObjectFlags.Reference && (type as TypeReference).node) { // Deferred type references are tracked through their associated AST node. This gives us finer // granularity than using their associated target because each manifest type reference has a diff --git a/tests/baselines/reference/issue52912.errors.txt b/tests/baselines/reference/issue52912.errors.txt new file mode 100644 index 0000000000000..84ceb99108765 --- /dev/null +++ b/tests/baselines/reference/issue52912.errors.txt @@ -0,0 +1,77 @@ +issue52912.ts(18,7): error TS2322: Type 'Source1' is not assignable to type 'Target1'. + Types of property 'array' are incompatible. + Type 'Source2[]' is not assignable to type 'Target2[]'. + Type 'Source2' is not assignable to type 'Target2'. + Types of property 'array' are incompatible. + Type 'Source3[]' is not assignable to type 'Target3[]'. + Type 'Source3' is not assignable to type 'Target3'. + Types of property 'array' are incompatible. + Type 'Source4[]' is not assignable to type 'Target4[]'. + Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +issue52912.ts(19,7): error TS2322: Type 'Source2' is not assignable to type 'Target2'. + Types of property 'array' are incompatible. + Type 'Source3[]' is not assignable to type 'Target3[]'. + Type 'Source3' is not assignable to type 'Target3'. + Types of property 'array' are incompatible. + Type 'Source4[]' is not assignable to type 'Target4[]'. + Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +issue52912.ts(20,7): error TS2322: Type 'Source3' is not assignable to type 'Target3'. + Types of property 'array' are incompatible. + Type 'Source4[]' is not assignable to type 'Target4[]'. + Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +issue52912.ts(21,7): error TS2741: Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. + + +==== issue52912.ts (4 errors) ==== + // Repro from #52912 + + type Source1 = { array: Source2[] }; + type Source2 = { array: Source3[] }; + type Source3 = { array: Source4[] }; + type Source4 = {}; + + type Target1 = { array: Target2[] }; + type Target2 = { array: Target3[] }; + type Target3 = { array: Target4[] }; + type Target4 = { someNewProperty: string }; + + declare const source1: Source1; + declare const source2: Source2; + declare const source3: Source3; + declare const source4: Source4; + + const target1: Target1 = source1; // Error + ~~~~~~~ +!!! error TS2322: Type 'Source1' is not assignable to type 'Target1'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source2[]' is not assignable to type 'Target2[]'. +!!! error TS2322: Type 'Source2' is not assignable to type 'Target2'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source3[]' is not assignable to type 'Target3[]'. +!!! error TS2322: Type 'Source3' is not assignable to type 'Target3'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source4[]' is not assignable to type 'Target4[]'. +!!! error TS2322: Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +!!! related TS2728 issue52912.ts:11:18: 'someNewProperty' is declared here. + const target2: Target2 = source2; // Error + ~~~~~~~ +!!! error TS2322: Type 'Source2' is not assignable to type 'Target2'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source3[]' is not assignable to type 'Target3[]'. +!!! error TS2322: Type 'Source3' is not assignable to type 'Target3'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source4[]' is not assignable to type 'Target4[]'. +!!! error TS2322: Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +!!! related TS2728 issue52912.ts:11:18: 'someNewProperty' is declared here. + const target3: Target3 = source3; // Error + ~~~~~~~ +!!! error TS2322: Type 'Source3' is not assignable to type 'Target3'. +!!! error TS2322: Types of property 'array' are incompatible. +!!! error TS2322: Type 'Source4[]' is not assignable to type 'Target4[]'. +!!! error TS2322: Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +!!! related TS2728 issue52912.ts:11:18: 'someNewProperty' is declared here. + const target4: Target4 = source4; // Error + ~~~~~~~ +!!! error TS2741: Property 'someNewProperty' is missing in type 'Source4' but required in type 'Target4'. +!!! related TS2728 issue52912.ts:11:18: 'someNewProperty' is declared here. + \ No newline at end of file diff --git a/tests/baselines/reference/issue52912.js b/tests/baselines/reference/issue52912.js new file mode 100644 index 0000000000000..9130118863c77 --- /dev/null +++ b/tests/baselines/reference/issue52912.js @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/issue52912.ts] //// + +//// [issue52912.ts] +// Repro from #52912 + +type Source1 = { array: Source2[] }; +type Source2 = { array: Source3[] }; +type Source3 = { array: Source4[] }; +type Source4 = {}; + +type Target1 = { array: Target2[] }; +type Target2 = { array: Target3[] }; +type Target3 = { array: Target4[] }; +type Target4 = { someNewProperty: string }; + +declare const source1: Source1; +declare const source2: Source2; +declare const source3: Source3; +declare const source4: Source4; + +const target1: Target1 = source1; // Error +const target2: Target2 = source2; // Error +const target3: Target3 = source3; // Error +const target4: Target4 = source4; // Error + + +//// [issue52912.js] +"use strict"; +// Repro from #52912 +const target1 = source1; // Error +const target2 = source2; // Error +const target3 = source3; // Error +const target4 = source4; // Error diff --git a/tests/baselines/reference/issue52912.symbols b/tests/baselines/reference/issue52912.symbols new file mode 100644 index 0000000000000..781d19e0fba5f --- /dev/null +++ b/tests/baselines/reference/issue52912.symbols @@ -0,0 +1,78 @@ +//// [tests/cases/compiler/issue52912.ts] //// + +=== issue52912.ts === +// Repro from #52912 + +type Source1 = { array: Source2[] }; +>Source1 : Symbol(Source1, Decl(issue52912.ts, 0, 0)) +>array : Symbol(array, Decl(issue52912.ts, 2, 16)) +>Source2 : Symbol(Source2, Decl(issue52912.ts, 2, 36)) + +type Source2 = { array: Source3[] }; +>Source2 : Symbol(Source2, Decl(issue52912.ts, 2, 36)) +>array : Symbol(array, Decl(issue52912.ts, 3, 16)) +>Source3 : Symbol(Source3, Decl(issue52912.ts, 3, 36)) + +type Source3 = { array: Source4[] }; +>Source3 : Symbol(Source3, Decl(issue52912.ts, 3, 36)) +>array : Symbol(array, Decl(issue52912.ts, 4, 16)) +>Source4 : Symbol(Source4, Decl(issue52912.ts, 4, 36)) + +type Source4 = {}; +>Source4 : Symbol(Source4, Decl(issue52912.ts, 4, 36)) + +type Target1 = { array: Target2[] }; +>Target1 : Symbol(Target1, Decl(issue52912.ts, 5, 18)) +>array : Symbol(array, Decl(issue52912.ts, 7, 16)) +>Target2 : Symbol(Target2, Decl(issue52912.ts, 7, 36)) + +type Target2 = { array: Target3[] }; +>Target2 : Symbol(Target2, Decl(issue52912.ts, 7, 36)) +>array : Symbol(array, Decl(issue52912.ts, 8, 16)) +>Target3 : Symbol(Target3, Decl(issue52912.ts, 8, 36)) + +type Target3 = { array: Target4[] }; +>Target3 : Symbol(Target3, Decl(issue52912.ts, 8, 36)) +>array : Symbol(array, Decl(issue52912.ts, 9, 16)) +>Target4 : Symbol(Target4, Decl(issue52912.ts, 9, 36)) + +type Target4 = { someNewProperty: string }; +>Target4 : Symbol(Target4, Decl(issue52912.ts, 9, 36)) +>someNewProperty : Symbol(someNewProperty, Decl(issue52912.ts, 10, 16)) + +declare const source1: Source1; +>source1 : Symbol(source1, Decl(issue52912.ts, 12, 13)) +>Source1 : Symbol(Source1, Decl(issue52912.ts, 0, 0)) + +declare const source2: Source2; +>source2 : Symbol(source2, Decl(issue52912.ts, 13, 13)) +>Source2 : Symbol(Source2, Decl(issue52912.ts, 2, 36)) + +declare const source3: Source3; +>source3 : Symbol(source3, Decl(issue52912.ts, 14, 13)) +>Source3 : Symbol(Source3, Decl(issue52912.ts, 3, 36)) + +declare const source4: Source4; +>source4 : Symbol(source4, Decl(issue52912.ts, 15, 13)) +>Source4 : Symbol(Source4, Decl(issue52912.ts, 4, 36)) + +const target1: Target1 = source1; // Error +>target1 : Symbol(target1, Decl(issue52912.ts, 17, 5)) +>Target1 : Symbol(Target1, Decl(issue52912.ts, 5, 18)) +>source1 : Symbol(source1, Decl(issue52912.ts, 12, 13)) + +const target2: Target2 = source2; // Error +>target2 : Symbol(target2, Decl(issue52912.ts, 18, 5)) +>Target2 : Symbol(Target2, Decl(issue52912.ts, 7, 36)) +>source2 : Symbol(source2, Decl(issue52912.ts, 13, 13)) + +const target3: Target3 = source3; // Error +>target3 : Symbol(target3, Decl(issue52912.ts, 19, 5)) +>Target3 : Symbol(Target3, Decl(issue52912.ts, 8, 36)) +>source3 : Symbol(source3, Decl(issue52912.ts, 14, 13)) + +const target4: Target4 = source4; // Error +>target4 : Symbol(target4, Decl(issue52912.ts, 20, 5)) +>Target4 : Symbol(Target4, Decl(issue52912.ts, 9, 36)) +>source4 : Symbol(source4, Decl(issue52912.ts, 15, 13)) + diff --git a/tests/baselines/reference/issue52912.types b/tests/baselines/reference/issue52912.types new file mode 100644 index 0000000000000..6ea0f348d9139 --- /dev/null +++ b/tests/baselines/reference/issue52912.types @@ -0,0 +1,91 @@ +//// [tests/cases/compiler/issue52912.ts] //// + +=== issue52912.ts === +// Repro from #52912 + +type Source1 = { array: Source2[] }; +>Source1 : Source1 +> : ^^^^^^^ +>array : Source2[] +> : ^^^^^^^^^ + +type Source2 = { array: Source3[] }; +>Source2 : Source2 +> : ^^^^^^^ +>array : Source3[] +> : ^^^^^^^^^ + +type Source3 = { array: Source4[] }; +>Source3 : Source3 +> : ^^^^^^^ +>array : Source4[] +> : ^^^^^^^^^ + +type Source4 = {}; +>Source4 : Source4 +> : ^^^^^^^ + +type Target1 = { array: Target2[] }; +>Target1 : Target1 +> : ^^^^^^^ +>array : Target2[] +> : ^^^^^^^^^ + +type Target2 = { array: Target3[] }; +>Target2 : Target2 +> : ^^^^^^^ +>array : Target3[] +> : ^^^^^^^^^ + +type Target3 = { array: Target4[] }; +>Target3 : Target3 +> : ^^^^^^^ +>array : Target4[] +> : ^^^^^^^^^ + +type Target4 = { someNewProperty: string }; +>Target4 : Target4 +> : ^^^^^^^ +>someNewProperty : string +> : ^^^^^^ + +declare const source1: Source1; +>source1 : Source1 +> : ^^^^^^^ + +declare const source2: Source2; +>source2 : Source2 +> : ^^^^^^^ + +declare const source3: Source3; +>source3 : Source3 +> : ^^^^^^^ + +declare const source4: Source4; +>source4 : Source4 +> : ^^^^^^^ + +const target1: Target1 = source1; // Error +>target1 : Target1 +> : ^^^^^^^ +>source1 : Source1 +> : ^^^^^^^ + +const target2: Target2 = source2; // Error +>target2 : Target2 +> : ^^^^^^^ +>source2 : Source2 +> : ^^^^^^^ + +const target3: Target3 = source3; // Error +>target3 : Target3 +> : ^^^^^^^ +>source3 : Source3 +> : ^^^^^^^ + +const target4: Target4 = source4; // Error +>target4 : Target4 +> : ^^^^^^^ +>source4 : Source4 +> : ^^^^^^^ + diff --git a/tests/cases/compiler/issue52912.ts b/tests/cases/compiler/issue52912.ts new file mode 100644 index 0000000000000..0002b9033c393 --- /dev/null +++ b/tests/cases/compiler/issue52912.ts @@ -0,0 +1,23 @@ +// @strict: true + +// Repro from #52912 + +type Source1 = { array: Source2[] }; +type Source2 = { array: Source3[] }; +type Source3 = { array: Source4[] }; +type Source4 = {}; + +type Target1 = { array: Target2[] }; +type Target2 = { array: Target3[] }; +type Target3 = { array: Target4[] }; +type Target4 = { someNewProperty: string }; + +declare const source1: Source1; +declare const source2: Source2; +declare const source3: Source3; +declare const source4: Source4; + +const target1: Target1 = source1; // Error +const target2: Target2 = source2; // Error +const target3: Target3 = source3; // Error +const target4: Target4 = source4; // Error