diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 390c843b0c968..639d0ab4c96d9 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; } @@ -45047,7 +45059,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Don't validate for-in initializer as it is already an error const widenedType = getWidenedTypeForVariableLikeDeclaration(node); if (needCheckInitializer) { + contextualBindingPatterns.push(node.name); const initializerType = checkExpressionCached(node.initializer); + contextualBindingPatterns.pop(); if (strictNullChecks && needCheckWidenedType) { checkNonNullNonVoidType(initializerType, node); } diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt b/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt new file mode 100644 index 0000000000000..ca54c0d2c73dc --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.errors.txt @@ -0,0 +1,50 @@ +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(2,7): error TS2322: Type '{ a: number; f: any; }' is not assignable to type 'string | number'. +destructuringContextualBindingStackOverflow.ts(2,9): error TS2339: Property 'a' does not exist on type 'string | number'. +destructuringContextualBindingStackOverflow.ts(2,12): error TS2339: Property 'f' does not exist on type 'string | number'. +destructuringContextualBindingStackOverflow.ts(2,55): error TS2448: Block-scoped variable 'f1' used before its declaration. +destructuringContextualBindingStackOverflow.ts(3,7): error TS2322: Type '{ a: any; f: any; }' is not assignable to type 'string | number'. +destructuringContextualBindingStackOverflow.ts(3,9): error TS2339: Property 'a' does not exist on type 'string | number'. +destructuringContextualBindingStackOverflow.ts(3,16): error TS2339: Property 'f' does not exist on type 'string | number'. +destructuringContextualBindingStackOverflow.ts(3,48): error TS2448: Block-scoped variable 'f2' used before its declaration. +destructuringContextualBindingStackOverflow.ts(3,55): error TS2448: Block-scoped variable 'a1' used before its declaration. + + +==== destructuringContextualBindingStackOverflow.ts (13 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. + const { a, f: f1 }: string | number = { a: 0, f: (1 + f1) }; + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ a: number; f: any; }' is not assignable to type 'string | number'. + ~ +!!! error TS2339: Property 'a' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number'. + ~~ +!!! error TS2448: Block-scoped variable 'f1' used before its declaration. +!!! related TS2728 destructuringContextualBindingStackOverflow.ts:2:15: 'f1' is declared here. + const { a: a1, f: f2 }: string | number = { a: f2, f: a1 }; + ~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ a: any; f: any; }' is not assignable to type 'string | number'. + ~ +!!! error TS2339: Property 'a' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number'. + ~~ +!!! error TS2448: Block-scoped variable 'f2' used before its declaration. +!!! related TS2728 destructuringContextualBindingStackOverflow.ts:3:19: 'f2' is declared here. + ~~ +!!! error TS2448: Block-scoped variable 'a1' used before its declaration. +!!! related TS2728 destructuringContextualBindingStackOverflow.ts:3:12: 'a1' 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 0000000000000..9e03fadbbd5cf --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.js @@ -0,0 +1,13 @@ +//// [tests/cases/compiler/destructuringContextualBindingStackOverflow.ts] //// + +//// [destructuringContextualBindingStackOverflow.ts] +const { c, f }: string | number | symbol = { c: 0, f }; +const { a, f: f1 }: string | number = { a: 0, f: (1 + f1) }; +const { a: a1, f: f2 }: string | number = { a: f2, f: a1 }; + + +//// [destructuringContextualBindingStackOverflow.js] +"use strict"; +var _a = { c: 0, f: f }, c = _a.c, f = _a.f; +var _b = { a: 0, f: (1 + f1) }, a = _b.a, f1 = _b.f; +var _c = { a: f2, f: a1 }, a1 = _c.a, f2 = _c.f; diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols b/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols new file mode 100644 index 0000000000000..45852adeb76c4 --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.symbols @@ -0,0 +1,24 @@ +//// [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)) + +const { a, f: f1 }: string | number = { a: 0, f: (1 + f1) }; +>a : Symbol(a, Decl(destructuringContextualBindingStackOverflow.ts, 1, 7)) +>f1 : Symbol(f1, Decl(destructuringContextualBindingStackOverflow.ts, 1, 10)) +>a : Symbol(a, Decl(destructuringContextualBindingStackOverflow.ts, 1, 39)) +>f : Symbol(f, Decl(destructuringContextualBindingStackOverflow.ts, 1, 45)) +>f1 : Symbol(f1, Decl(destructuringContextualBindingStackOverflow.ts, 1, 10)) + +const { a: a1, f: f2 }: string | number = { a: f2, f: a1 }; +>a1 : Symbol(a1, Decl(destructuringContextualBindingStackOverflow.ts, 2, 7)) +>f2 : Symbol(f2, Decl(destructuringContextualBindingStackOverflow.ts, 2, 14)) +>a : Symbol(a, Decl(destructuringContextualBindingStackOverflow.ts, 2, 43)) +>f2 : Symbol(f2, Decl(destructuringContextualBindingStackOverflow.ts, 2, 14)) +>f : Symbol(f, Decl(destructuringContextualBindingStackOverflow.ts, 2, 50)) +>a1 : Symbol(a1, Decl(destructuringContextualBindingStackOverflow.ts, 2, 7)) + diff --git a/tests/baselines/reference/destructuringContextualBindingStackOverflow.types b/tests/baselines/reference/destructuringContextualBindingStackOverflow.types new file mode 100644 index 0000000000000..d25524462433b --- /dev/null +++ b/tests/baselines/reference/destructuringContextualBindingStackOverflow.types @@ -0,0 +1,61 @@ +//// [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 +> : ^^^ + +const { a, f: f1 }: string | number = { a: 0, f: (1 + f1) }; +>a : any +> : ^^^ +>f : any +> : ^^^ +>f1 : any +> : ^^^ +>{ a: 0, f: (1 + f1) } : { a: number; f: any; } +> : ^^^^^^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>0 : 0 +> : ^ +>f : any +> : ^^^ +>(1 + f1) : any +> : ^^^ +>1 + f1 : any +> : ^^^ +>1 : 1 +> : ^ +>f1 : any +> : ^^^ + +const { a: a1, f: f2 }: string | number = { a: f2, f: a1 }; +>a : any +> : ^^^ +>a1 : any +> : ^^^ +>f : any +> : ^^^ +>f2 : any +> : ^^^ +>{ a: f2, f: a1 } : { a: any; f: any; } +> : ^^^^^^^^^^^^^^^^^^^ +>a : any +> : ^^^ +>f2 : any +> : ^^^ +>f : any +> : ^^^ +>a1 : any +> : ^^^ + diff --git a/tests/cases/compiler/destructuringContextualBindingStackOverflow.ts b/tests/cases/compiler/destructuringContextualBindingStackOverflow.ts new file mode 100644 index 0000000000000..6aa9592848df6 --- /dev/null +++ b/tests/cases/compiler/destructuringContextualBindingStackOverflow.ts @@ -0,0 +1,5 @@ +// @strict: true + +const { c, f }: string | number | symbol = { c: 0, f }; +const { a, f: f1 }: string | number = { a: 0, f: (1 + f1) }; +const { a: a1, f: f2 }: string | number = { a: f2, f: a1 };