Fix stack overflow in binding element type resolution with circular references

Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-01-16 19:59:05 +00:00
parent def00df7a2
commit 9c7eea55ef
5 changed files with 228 additions and 2 deletions

View File

@ -11689,8 +11689,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
parentType = getNonNullableType(parentType);
}
// Filter `undefined` from the type we check against if the parent has an initializer and that initializer is not possibly `undefined`
else if (strictNullChecks && pattern.parent.initializer && !(hasTypeFacts(getTypeOfInitializer(pattern.parent.initializer), TypeFacts.EQUndefined))) {
parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined);
// Use a non-recursive type check to avoid stack overflow when the initializer references binding elements
else if (strictNullChecks && pattern.parent.initializer) {
const initializerType = getQuickTypeOfExpression(pattern.parent.initializer) || getNodeLinks(pattern.parent.initializer).resolvedType;
if (initializerType && !(hasTypeFacts(initializerType, TypeFacts.EQUndefined))) {
parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined);
}
}
const accessFlags = AccessFlags.ExpressionPosition | (noTupleBoundsCheck || hasDefaultValue(declaration) ? AccessFlags.AllowMissing : 0);

View File

@ -0,0 +1,80 @@
circularBindingElementTypeResolution.ts(2,7): error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number'.
circularBindingElementTypeResolution.ts(2,9): error TS2339: Property 'c' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(2,12): error TS2339: Property 'f' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(2,43): error TS2448: Block-scoped variable 'f' used before its declaration.
circularBindingElementTypeResolution.ts(5,7): error TS2322: Type '{ a: number; f2: any; }' is not assignable to type 'string | number'.
circularBindingElementTypeResolution.ts(5,9): error TS2339: Property 'a' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(5,12): error TS2339: Property 'f2' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(5,53): error TS2448: Block-scoped variable 'f2' used before its declaration.
circularBindingElementTypeResolution.ts(8,7): error TS2322: Type '{ a2: any; f3: any; }' is not assignable to type 'string | number'.
circularBindingElementTypeResolution.ts(8,9): error TS2339: Property 'a2' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(8,13): error TS2339: Property 'f3' does not exist on type 'string | number'.
circularBindingElementTypeResolution.ts(8,43): error TS2448: Block-scoped variable 'f3' used before its declaration.
circularBindingElementTypeResolution.ts(8,51): error TS2448: Block-scoped variable 'a2' used before its declaration.
circularBindingElementTypeResolution.ts(11,80): error TS2322: Type 'string' is not assignable to type 'number'.
circularBindingElementTypeResolution.ts(11,83): error TS2448: Block-scoped variable 'y' used before its declaration.
circularBindingElementTypeResolution.ts(11,83): error TS2454: Variable 'y' is used before being assigned.
circularBindingElementTypeResolution.ts(11,86): error TS2322: Type 'number' is not assignable to type 'string'.
circularBindingElementTypeResolution.ts(11,89): error TS2448: Block-scoped variable 'x' used before its declaration.
circularBindingElementTypeResolution.ts(11,89): error TS2454: Variable 'x' is used before being assigned.
==== circularBindingElementTypeResolution.ts (19 errors) ====
// Test case 1: Simple self-reference with shorthand property
const { c, f }: string | number = { c: 0, f };
~~~~~~~~
!!! error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number'.
~
!!! error TS2339: Property 'c' does not exist on type 'string | number'.
~
!!! error TS2339: Property 'f' does not exist on type 'string | number'.
~
!!! error TS2448: Block-scoped variable 'f' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:2:12: 'f' is declared here.
// Test case 2: Self-reference with expression
const { a, f2 }: string | number = { a: 0, f2: (1 + f2) };
~~~~~~~~~
!!! error TS2322: Type '{ a: number; f2: any; }' is not assignable to type 'string | number'.
~
!!! error TS2339: Property 'a' does not exist on type 'string | number'.
~~
!!! error TS2339: Property 'f2' does not exist on type 'string | number'.
~~
!!! error TS2448: Block-scoped variable 'f2' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:5:12: 'f2' is declared here.
// Test case 3: Circular reference between two binding elements
const { a2, f3 }: string | number = { a2: f3, f3: a2 };
~~~~~~~~~~
!!! error TS2322: Type '{ a2: any; f3: any; }' is not assignable to type 'string | number'.
~~
!!! error TS2339: Property 'a2' does not exist on type 'string | number'.
~~
!!! error TS2339: Property 'f3' does not exist on type 'string | number'.
~~
!!! error TS2448: Block-scoped variable 'f3' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:8:13: 'f3' is declared here.
~~
!!! error TS2448: Block-scoped variable 'a2' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:8:9: 'a2' is declared here.
// Test case 4: Nested destructuring with self-reference
const { nested: { x, y } }: { nested: { x: number, y: string } } = { nested: { x: y, y: x } };
~
!!! error TS2322: Type 'string' is not assignable to type 'number'.
!!! related TS6500 circularBindingElementTypeResolution.ts:11:41: The expected type comes from property 'x' which is declared here on type '{ x: number; y: string; }'
~
!!! error TS2448: Block-scoped variable 'y' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:11:22: 'y' is declared here.
~
!!! error TS2454: Variable 'y' is used before being assigned.
~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! related TS6500 circularBindingElementTypeResolution.ts:11:52: The expected type comes from property 'y' which is declared here on type '{ x: number; y: string; }'
~
!!! error TS2448: Block-scoped variable 'x' used before its declaration.
!!! related TS2728 circularBindingElementTypeResolution.ts:11:19: 'x' is declared here.
~
!!! error TS2454: Variable 'x' is used before being assigned.

View File

@ -0,0 +1,41 @@
//// [tests/cases/compiler/circularBindingElementTypeResolution.ts] ////
=== circularBindingElementTypeResolution.ts ===
// Test case 1: Simple self-reference with shorthand property
const { c, f }: string | number = { c: 0, f };
>c : Symbol(c, Decl(circularBindingElementTypeResolution.ts, 1, 7))
>f : Symbol(f, Decl(circularBindingElementTypeResolution.ts, 1, 10))
>c : Symbol(c, Decl(circularBindingElementTypeResolution.ts, 1, 35))
>f : Symbol(f, Decl(circularBindingElementTypeResolution.ts, 1, 41))
// Test case 2: Self-reference with expression
const { a, f2 }: string | number = { a: 0, f2: (1 + f2) };
>a : Symbol(a, Decl(circularBindingElementTypeResolution.ts, 4, 7))
>f2 : Symbol(f2, Decl(circularBindingElementTypeResolution.ts, 4, 10))
>a : Symbol(a, Decl(circularBindingElementTypeResolution.ts, 4, 36))
>f2 : Symbol(f2, Decl(circularBindingElementTypeResolution.ts, 4, 42))
>f2 : Symbol(f2, Decl(circularBindingElementTypeResolution.ts, 4, 10))
// Test case 3: Circular reference between two binding elements
const { a2, f3 }: string | number = { a2: f3, f3: a2 };
>a2 : Symbol(a2, Decl(circularBindingElementTypeResolution.ts, 7, 7))
>f3 : Symbol(f3, Decl(circularBindingElementTypeResolution.ts, 7, 11))
>a2 : Symbol(a2, Decl(circularBindingElementTypeResolution.ts, 7, 37))
>f3 : Symbol(f3, Decl(circularBindingElementTypeResolution.ts, 7, 11))
>f3 : Symbol(f3, Decl(circularBindingElementTypeResolution.ts, 7, 45))
>a2 : Symbol(a2, Decl(circularBindingElementTypeResolution.ts, 7, 7))
// Test case 4: Nested destructuring with self-reference
const { nested: { x, y } }: { nested: { x: number, y: string } } = { nested: { x: y, y: x } };
>nested : Symbol(nested, Decl(circularBindingElementTypeResolution.ts, 10, 29))
>x : Symbol(x, Decl(circularBindingElementTypeResolution.ts, 10, 17))
>y : Symbol(y, Decl(circularBindingElementTypeResolution.ts, 10, 20))
>nested : Symbol(nested, Decl(circularBindingElementTypeResolution.ts, 10, 29))
>x : Symbol(x, Decl(circularBindingElementTypeResolution.ts, 10, 39))
>y : Symbol(y, Decl(circularBindingElementTypeResolution.ts, 10, 50))
>nested : Symbol(nested, Decl(circularBindingElementTypeResolution.ts, 10, 68))
>x : Symbol(x, Decl(circularBindingElementTypeResolution.ts, 10, 78))
>y : Symbol(y, Decl(circularBindingElementTypeResolution.ts, 10, 20))
>y : Symbol(y, Decl(circularBindingElementTypeResolution.ts, 10, 84))
>x : Symbol(x, Decl(circularBindingElementTypeResolution.ts, 10, 17))

View File

@ -0,0 +1,87 @@
//// [tests/cases/compiler/circularBindingElementTypeResolution.ts] ////
=== circularBindingElementTypeResolution.ts ===
// Test case 1: Simple self-reference with shorthand property
const { c, f }: string | number = { c: 0, f };
>c : any
> : ^^^
>f : any
> : ^^^
>{ c: 0, f } : { c: number; f: any; }
> : ^^^^^^^^^^^^^^^^^^^^^^
>c : number
> : ^^^^^^
>0 : 0
> : ^
>f : any
> : ^^^
// Test case 2: Self-reference with expression
const { a, f2 }: string | number = { a: 0, f2: (1 + f2) };
>a : any
> : ^^^
>f2 : any
> : ^^^
>{ a: 0, f2: (1 + f2) } : { a: number; f2: any; }
> : ^^^^^^^^^^^^^^^^^^^^^^^
>a : number
> : ^^^^^^
>0 : 0
> : ^
>f2 : any
> : ^^^
>(1 + f2) : any
> : ^^^
>1 + f2 : any
> : ^^^
>1 : 1
> : ^
>f2 : any
> : ^^^
// Test case 3: Circular reference between two binding elements
const { a2, f3 }: string | number = { a2: f3, f3: a2 };
>a2 : any
> : ^^^
>f3 : any
> : ^^^
>{ a2: f3, f3: a2 } : { a2: any; f3: any; }
> : ^^^^^^^^^^^^^^^^^^^^^
>a2 : any
> : ^^^
>f3 : any
> : ^^^
>f3 : any
> : ^^^
>a2 : any
> : ^^^
// Test case 4: Nested destructuring with self-reference
const { nested: { x, y } }: { nested: { x: number, y: string } } = { nested: { x: y, y: x } };
>nested : any
> : ^^^
>x : number
> : ^^^^^^
>y : string
> : ^^^^^^
>nested : { x: number; y: string; }
> : ^^^^^ ^^^^^ ^^^
>x : number
> : ^^^^^^
>y : string
> : ^^^^^^
>{ nested: { x: y, y: x } } : { nested: { x: string; y: number; }; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>nested : { x: string; y: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^
>{ x: y, y: x } : { x: string; y: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^
>x : string
> : ^^^^^^
>y : string
> : ^^^^^^
>y : number
> : ^^^^^^
>x : number
> : ^^^^^^

View File

@ -0,0 +1,14 @@
// @strict: true
// @noEmit: true
// Test case 1: Simple self-reference with shorthand property
const { c, f }: string | number = { c: 0, f };
// Test case 2: Self-reference with expression
const { a, f2 }: string | number = { a: 0, f2: (1 + f2) };
// Test case 3: Circular reference between two binding elements
const { a2, f3 }: string | number = { a2: f3, f3: a2 };
// Test case 4: Nested destructuring with self-reference
const { nested: { x, y } }: { nested: { x: number, y: string } } = { nested: { x: y, y: x } };