From 2abf5a1a7b330116a60318b084695544fb830e37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:18:37 +0000 Subject: [PATCH] Guard destructuring initializer contextual typing Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/checker.ts | 16 ++++++++++++++-- ...ngContextualBindingStackOverflow.errors.txt | 18 ++++++++++++++++++ ...tructuringContextualBindingStackOverflow.js | 9 +++++++++ ...uringContextualBindingStackOverflow.symbols | 9 +++++++++ ...cturingContextualBindingStackOverflow.types | 17 +++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt create mode 100644 tests/baselines/reference/destructuringContextualBindingStackOverflow.js create mode 100644 tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols create mode 100644 tests/baselines/reference/destructuringContextualBindingStackOverflow.types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5638bf4e32d..639d0ab4c96 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28312,7 +28312,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // from its initializer, we'll already have cached the type. Otherwise we compute it now // without caching such that transient types are reflected. const links = getNodeLinks(node); - return links.resolvedType || getTypeOfExpression(node); + if (links.resolvedType) { + return links.resolvedType; + } + const pattern = isVariableDeclaration(node.parent) && node.parent.initializer === node && isBindingPattern(node.parent.name) + ? node.parent.name + : undefined; + if (pattern) { + contextualBindingPatterns.push(pattern); + const type = getTypeOfExpression(node); + contextualBindingPatterns.pop(); + return type; + } + return getTypeOfExpression(node); } function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) { @@ -31023,7 +31035,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If the identifier is declared in a binding pattern for which we're currently computing the implied type and the // reference occurs with the same binding pattern, return the non-inferrable any type. This for example occurs in // 'const [a, b = a + 1] = [2]' when we're computing the contextual type for the array literal '[2]'. - if (declaration && declaration.kind === SyntaxKind.BindingElement && contains(contextualBindingPatterns, declaration.parent) && findAncestor(node, parent => parent === declaration!.parent)) { + if (declaration && declaration.kind === SyntaxKind.BindingElement && contains(contextualBindingPatterns, declaration.parent)) { return nonInferrableAnyType; } diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt b/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt new file mode 100644 index 00000000000..be4d1692381 --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt @@ -0,0 +1,18 @@ +destructuringContextualBindingStackOverflow.ts(1,7): error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number | symbol'. +destructuringContextualBindingStackOverflow.ts(1,9): error TS2339: Property 'c' does not exist on type 'string | number | symbol'. +destructuringContextualBindingStackOverflow.ts(1,12): error TS2339: Property 'f' does not exist on type 'string | number | symbol'. +destructuringContextualBindingStackOverflow.ts(1,52): error TS2448: Block-scoped variable 'f' used before its declaration. + + +==== destructuringContextualBindingStackOverflow.ts (4 errors) ==== + const { c, f }: string | number | symbol = { c: 0, f }; + ~~~~~~~~ +!!! error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number | symbol'. + ~ +!!! error TS2339: Property 'c' does not exist on type 'string | number | symbol'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number | symbol'. + ~ +!!! error TS2448: Block-scoped variable 'f' used before its declaration. +!!! related TS2728 destructuringContextualBindingStackOverflow.ts:1:12: 'f' is declared here. + \ No newline at end of file diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.js b/tests/baselines/reference/destructuringContextualBindingStackOverflow.js new file mode 100644 index 00000000000..9d1de147d23 --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/destructuringContextualBindingStackOverflow.ts] //// + +//// [destructuringContextualBindingStackOverflow.ts] +const { c, f }: string | number | symbol = { c: 0, f }; + + +//// [destructuringContextualBindingStackOverflow.js] +"use strict"; +var _a = { c: 0, f: f }, c = _a.c, f = _a.f; diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols b/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols new file mode 100644 index 00000000000..1d8fc2524a4 --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/destructuringContextualBindingStackOverflow.ts] //// + +=== destructuringContextualBindingStackOverflow.ts === +const { c, f }: string | number | symbol = { c: 0, f }; +>c : Symbol(c, Decl(destructuringContextualBindingStackOverflow.ts, 0, 7)) +>f : Symbol(f, Decl(destructuringContextualBindingStackOverflow.ts, 0, 10)) +>c : Symbol(c, Decl(destructuringContextualBindingStackOverflow.ts, 0, 44)) +>f : Symbol(f, Decl(destructuringContextualBindingStackOverflow.ts, 0, 50)) + diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.types b/tests/baselines/reference/destructuringContextualBindingStackOverflow.types new file mode 100644 index 00000000000..a1d79026336 --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/destructuringContextualBindingStackOverflow.ts] //// + +=== destructuringContextualBindingStackOverflow.ts === +const { c, f }: string | number | symbol = { c: 0, f }; +>c : any +> : ^^^ +>f : any +> : ^^^ +>{ c: 0, f } : { c: number; f: any; } +> : ^^^^^^^^^^^^^^^^^^^^^^ +>c : number +> : ^^^^^^ +>0 : 0 +> : ^ +>f : any +> : ^^^ +