From df471c03cc67b86f12ecb772d8dc7f9e53dfa6a1 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 8 Dec 2023 12:17:36 +0000 Subject: [PATCH 1/8] Improve binding element type inference Signed-off-by: Babak K. Shandiz --- src/compiler/checker.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 56c4d8a2114c9..3ab8bd8fb6af0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11416,7 +11416,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // contextual type or, if the element itself is a binding pattern, with the type implied by that binding // pattern. const contextualType = isBindingPattern(element.name) ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false) : unknownType; - return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, CheckMode.Normal, contextualType))); + return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, reportErrors ? CheckMode.Normal : CheckMode.Contextual, contextualType))); } if (isBindingPattern(element.name)) { return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); @@ -11574,16 +11574,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { + function getTypeOfVariableOrParameterOrProperty(symbol: Symbol, checkMode?: CheckMode): Type { const links = getSymbolLinks(symbol); if (!links.type) { - const type = getTypeOfVariableOrParameterOrPropertyWorker(symbol); + const type = getTypeOfVariableOrParameterOrPropertyWorker(symbol, checkMode); // For a contextually typed parameter it is possible that a type has already // been assigned (in assignTypeToParameterAndFixTypeParameters), and we want // to preserve this type. In fact, we need to _prefer_ that type, but it won't // be assigned until contextual typing is complete, so we need to defer in // cases where contextual typing may take place. - if (!links.type && !isParameterOfContextSensitiveSignature(symbol)) { + if (!links.type && !isParameterOfContextSensitiveSignature(symbol) && !(checkMode !== CheckMode.Normal && checkMode)) { links.type = type; } return type; @@ -11591,7 +11591,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.type; } - function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol): Type { + function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol, checkMode?: CheckMode): Type { // Handle prototype property if (symbol.flags & SymbolFlags.Prototype) { return getTypeOfPrototypeProperty(symbol); @@ -11634,6 +11634,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { return getTypeOfFuncClassEnumModule(symbol); } + + if (checkMode !== CheckMode.Normal && checkMode) { + return anyType; + } return reportCircularityError(symbol); } let type: Type; @@ -11706,6 +11710,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (symbol.flags & SymbolFlags.ValueModule && !(symbol.flags & SymbolFlags.Assignment)) { return getTypeOfFuncClassEnumModule(symbol); } + + if (checkMode !== CheckMode.Normal && checkMode) { + return type; + } return reportCircularityError(symbol); } return type; @@ -11988,7 +11996,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfSymbol(symbol); } - function getTypeOfSymbol(symbol: Symbol): Type { + function getTypeOfSymbol(symbol: Symbol, checkMode?: CheckMode): Type { const checkFlags = getCheckFlags(symbol); if (checkFlags & CheckFlags.DeferredType) { return getTypeOfSymbolWithDeferredType(symbol); @@ -12003,7 +12011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfReverseMappedSymbol(symbol as ReverseMappedSymbol); } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { - return getTypeOfVariableOrParameterOrProperty(symbol); + return getTypeOfVariableOrParameterOrProperty(symbol, checkMode); } if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) { return getTypeOfFuncClassEnumModule(symbol); @@ -28823,8 +28831,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier) { - const type = getTypeOfSymbol(symbol); + function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier, checkMode?: CheckMode) { + const type = getTypeOfSymbol(symbol, checkMode); const declaration = symbol.valueDeclaration; if (declaration) { // If we have a non-rest binding element with no initializer declared as a const variable or a const-like @@ -28987,7 +28995,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkNestedBlockScopedBinding(node, symbol); - let type = getNarrowedTypeOfSymbol(localOrExportSymbol, node); + let type = getNarrowedTypeOfSymbol(localOrExportSymbol, node, checkMode); const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind) { From 58db89a97b6eed3b95da204880530cd8d2f989f6 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 8 Dec 2023 12:21:12 +0000 Subject: [PATCH 2/8] Modify existing test case's '.types' file Signed-off-by: Babak K. Shandiz --- ...tructuringArrayBindingPatternAndAssignment3.types | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment3.types b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment3.types index 72aaa6378e5af..791ba83c243dc 100644 --- a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment3.types +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment3.types @@ -2,16 +2,16 @@ === destructuringArrayBindingPatternAndAssignment3.ts === const [a, b = a] = [1]; // ok ->a : any ->b : any ->a : any +>a : number +>b : number +>a : number >[1] : [number] >1 : 1 const [c, d = c, e = e] = [1]; // error for e = e ->c : any ->d : any ->c : any +>c : number +>d : number +>c : number >e : any >e : any >[1] : [number] From 354ebb07c2c809cba0e3583b9d9af11506c1e608 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Mon, 11 Dec 2023 13:42:43 +0000 Subject: [PATCH 3/8] Add tests for array binding pattern assignment Signed-off-by: Babak K. Shandiz --- ...PatternAndAssignment5SiblingInitializer.js | 38 +++++++++++ ...rnAndAssignment5SiblingInitializer.symbols | 48 +++++++++++++ ...ternAndAssignment5SiblingInitializer.types | 68 +++++++++++++++++++ ...PatternAndAssignment5SiblingInitializer.ts | 19 ++++++ 4 files changed, 173 insertions(+) create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types create mode 100644 tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js new file mode 100644 index 0000000000000..58d662d96f1b8 --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js @@ -0,0 +1,38 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts] //// + +//// [destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts] +// To be inferred as `number` +function f1() { + const [a1, b1 = a1] = [1]; + const [a2, b2 = 1 + a2] = [1]; +} + +// To be inferred as `string` +function f2() { + const [a1, b1 = a1] = ['hi']; + const [a2, b2 = a2 + '!'] = ['hi']; +} + +// To be inferred as `string | number` +function f3() { + const [a1, b1 = a1] = ['hi', 1]; + const [a2, b2 = a2 + '!'] = ['hi', 1]; +} + + +//// [destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js] +// To be inferred as `number` +function f1() { + var _a = [1], a1 = _a[0], _b = _a[1], b1 = _b === void 0 ? a1 : _b; + var _c = [1], a2 = _c[0], _d = _c[1], b2 = _d === void 0 ? 1 + a2 : _d; +} +// To be inferred as `string` +function f2() { + var _a = ['hi'], a1 = _a[0], _b = _a[1], b1 = _b === void 0 ? a1 : _b; + var _c = ['hi'], a2 = _c[0], _d = _c[1], b2 = _d === void 0 ? a2 + '!' : _d; +} +// To be inferred as `string | number` +function f3() { + var _a = ['hi', 1], a1 = _a[0], _b = _a[1], b1 = _b === void 0 ? a1 : _b; + var _c = ['hi', 1], a2 = _c[0], _d = _c[1], b2 = _d === void 0 ? a2 + '!' : _d; +} diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols new file mode 100644 index 0000000000000..115f9f7f0a6f8 --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols @@ -0,0 +1,48 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts] //// + +=== destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts === +// To be inferred as `number` +function f1() { +>f1 : Symbol(f1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 0, 0)) + + const [a1, b1 = a1] = [1]; +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 2, 11)) +>b1 : Symbol(b1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 2, 14)) +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 2, 11)) + + const [a2, b2 = 1 + a2] = [1]; +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 3, 11)) +>b2 : Symbol(b2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 3, 14)) +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 3, 11)) +} + +// To be inferred as `string` +function f2() { +>f2 : Symbol(f2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 4, 1)) + + const [a1, b1 = a1] = ['hi']; +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 8, 11)) +>b1 : Symbol(b1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 8, 14)) +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 8, 11)) + + const [a2, b2 = a2 + '!'] = ['hi']; +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 9, 11)) +>b2 : Symbol(b2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 9, 14)) +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 9, 11)) +} + +// To be inferred as `string | number` +function f3() { +>f3 : Symbol(f3, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 10, 1)) + + const [a1, b1 = a1] = ['hi', 1]; +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 14, 11)) +>b1 : Symbol(b1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 14, 14)) +>a1 : Symbol(a1, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 14, 11)) + + const [a2, b2 = a2 + '!'] = ['hi', 1]; +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 15, 11)) +>b2 : Symbol(b2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 15, 14)) +>a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 15, 11)) +} + diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types new file mode 100644 index 0000000000000..df11e27070dfb --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types @@ -0,0 +1,68 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts] //// + +=== destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts === +// To be inferred as `number` +function f1() { +>f1 : () => void + + const [a1, b1 = a1] = [1]; +>a1 : number +>b1 : number +>a1 : number +>[1] : [number] +>1 : 1 + + const [a2, b2 = 1 + a2] = [1]; +>a2 : number +>b2 : number +>1 + a2 : number +>1 : 1 +>a2 : number +>[1] : [number] +>1 : 1 +} + +// To be inferred as `string` +function f2() { +>f2 : () => void + + const [a1, b1 = a1] = ['hi']; +>a1 : string +>b1 : string +>a1 : string +>['hi'] : [string] +>'hi' : "hi" + + const [a2, b2 = a2 + '!'] = ['hi']; +>a2 : string +>b2 : string +>a2 + '!' : string +>a2 : string +>'!' : "!" +>['hi'] : [string] +>'hi' : "hi" +} + +// To be inferred as `string | number` +function f3() { +>f3 : () => void + + const [a1, b1 = a1] = ['hi', 1]; +>a1 : string +>b1 : string | number +>a1 : string +>['hi', 1] : [string, number] +>'hi' : "hi" +>1 : 1 + + const [a2, b2 = a2 + '!'] = ['hi', 1]; +>a2 : string +>b2 : string | number +>a2 + '!' : string +>a2 : string +>'!' : "!" +>['hi', 1] : [string, number] +>'hi' : "hi" +>1 : 1 +} + diff --git a/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts new file mode 100644 index 0000000000000..fecc40937fcd3 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts @@ -0,0 +1,19 @@ +// @noImplicitAny: true + +// To be inferred as `number` +function f1() { + const [a1, b1 = a1] = [1]; + const [a2, b2 = 1 + a2] = [1]; +} + +// To be inferred as `string` +function f2() { + const [a1, b1 = a1] = ['hi']; + const [a2, b2 = a2 + '!'] = ['hi']; +} + +// To be inferred as `string | number` +function f3() { + const [a1, b1 = a1] = ['hi', 1]; + const [a2, b2 = a2 + '!'] = ['hi', 1]; +} From 3f1b5244e41fa488cbef4180285961ffb4d41699 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Mon, 11 Dec 2023 13:43:21 +0000 Subject: [PATCH 4/8] Add tests for object binding pattern assignment Signed-off-by: Babak K. Shandiz --- ...PatternAndAssignment9SiblingInitializer.js | 37 +++++++++ ...rnAndAssignment9SiblingInitializer.symbols | 55 ++++++++++++++ ...ternAndAssignment9SiblingInitializer.types | 75 +++++++++++++++++++ ...PatternAndAssignment9SiblingInitializer.ts | 19 +++++ 4 files changed, 186 insertions(+) create mode 100644 tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js create mode 100644 tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols create mode 100644 tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types create mode 100644 tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js new file mode 100644 index 0000000000000..7ce61e4b8a1ac --- /dev/null +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js @@ -0,0 +1,37 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts] //// + +//// [destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts] +// To be inferred as `number` +function f1() { + const { a1, b1 = a1 } = { a1: 1 }; + const { a2, b2 = 1 + a2 } = { a2: 1 }; +} + +// To be inferred as `string` +function f2() { + const { a1, b1 = a1 } = { a1: 'hi' }; + const { a2, b2 = a2 + '!' } = { a2: 'hi' }; +} + +// To be inferred as `string | number` +function f3() { + const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; + const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; +} + +//// [destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js] +// To be inferred as `number` +function f1() { + var _a = { a1: 1 }, a1 = _a.a1, _b = _a.b1, b1 = _b === void 0 ? a1 : _b; + var _c = { a2: 1 }, a2 = _c.a2, _d = _c.b2, b2 = _d === void 0 ? 1 + a2 : _d; +} +// To be inferred as `string` +function f2() { + var _a = { a1: 'hi' }, a1 = _a.a1, _b = _a.b1, b1 = _b === void 0 ? a1 : _b; + var _c = { a2: 'hi' }, a2 = _c.a2, _d = _c.b2, b2 = _d === void 0 ? a2 + '!' : _d; +} +// To be inferred as `string | number` +function f3() { + var _a = { a1: 'hi', b1: 1 }, a1 = _a.a1, _b = _a.b1, b1 = _b === void 0 ? a1 : _b; + var _c = { a2: 'hi', b2: 1 }, a2 = _c.a2, _d = _c.b2, b2 = _d === void 0 ? a2 + '!' : _d; +} diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols new file mode 100644 index 0000000000000..57eb6eb863156 --- /dev/null +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols @@ -0,0 +1,55 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts] //// + +=== destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts === +// To be inferred as `number` +function f1() { +>f1 : Symbol(f1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 0, 0)) + + const { a1, b1 = a1 } = { a1: 1 }; +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 2, 11)) +>b1 : Symbol(b1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 2, 15)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 2, 11)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 2, 29)) + + const { a2, b2 = 1 + a2 } = { a2: 1 }; +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 3, 11)) +>b2 : Symbol(b2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 3, 15)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 3, 11)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 3, 33)) +} + +// To be inferred as `string` +function f2() { +>f2 : Symbol(f2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 4, 1)) + + const { a1, b1 = a1 } = { a1: 'hi' }; +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 8, 11)) +>b1 : Symbol(b1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 8, 15)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 8, 11)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 8, 29)) + + const { a2, b2 = a2 + '!' } = { a2: 'hi' }; +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 9, 11)) +>b2 : Symbol(b2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 9, 15)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 9, 11)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 9, 35)) +} + +// To be inferred as `string | number` +function f3() { +>f3 : Symbol(f3, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 10, 1)) + + const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 14, 11)) +>b1 : Symbol(b1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 14, 15)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 14, 11)) +>a1 : Symbol(a1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 14, 29)) +>b1 : Symbol(b1, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 14, 39)) + + const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 11)) +>b2 : Symbol(b2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 15)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 11)) +>a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 35)) +>b2 : Symbol(b2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 45)) +} diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types new file mode 100644 index 0000000000000..2b7bc7d2aa1b4 --- /dev/null +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types @@ -0,0 +1,75 @@ +//// [tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts] //// + +=== destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts === +// To be inferred as `number` +function f1() { +>f1 : () => void + + const { a1, b1 = a1 } = { a1: 1 }; +>a1 : number +>b1 : number +>a1 : number +>{ a1: 1 } : { a1: number; b1?: number; } +>a1 : number +>1 : 1 + + const { a2, b2 = 1 + a2 } = { a2: 1 }; +>a2 : number +>b2 : number +>1 + a2 : number +>1 : 1 +>a2 : number +>{ a2: 1 } : { a2: number; b2?: number; } +>a2 : number +>1 : 1 +} + +// To be inferred as `string` +function f2() { +>f2 : () => void + + const { a1, b1 = a1 } = { a1: 'hi' }; +>a1 : string +>b1 : string +>a1 : string +>{ a1: 'hi' } : { a1: string; b1?: string; } +>a1 : string +>'hi' : "hi" + + const { a2, b2 = a2 + '!' } = { a2: 'hi' }; +>a2 : string +>b2 : string +>a2 + '!' : string +>a2 : string +>'!' : "!" +>{ a2: 'hi' } : { a2: string; b2?: string; } +>a2 : string +>'hi' : "hi" +} + +// To be inferred as `string | number` +function f3() { +>f3 : () => void + + const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; +>a1 : string +>b1 : string | number +>a1 : string +>{ a1: 'hi', b1: 1 } : { a1: string; b1?: number; } +>a1 : string +>'hi' : "hi" +>b1 : number +>1 : 1 + + const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; +>a2 : string +>b2 : string | number +>a2 + '!' : string +>a2 : string +>'!' : "!" +>{ a2: 'hi', b2: 1 } : { a2: string; b2?: number; } +>a2 : string +>'hi' : "hi" +>b2 : number +>1 : 1 +} diff --git a/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts new file mode 100644 index 0000000000000..200e75e336389 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts @@ -0,0 +1,19 @@ +// @noImplicitAny: true + +// To be inferred as `number` +function f1() { + const { a1, b1 = a1 } = { a1: 1 }; + const { a2, b2 = 1 + a2 } = { a2: 1 }; +} + +// To be inferred as `string` +function f2() { + const { a1, b1 = a1 } = { a1: 'hi' }; + const { a2, b2 = a2 + '!' } = { a2: 'hi' }; +} + +// To be inferred as `string | number` +function f3() { + const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; + const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; +} \ No newline at end of file From d00a5b7e7c0ba16b41acd361f7d8f37736366d91 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Sat, 16 Dec 2023 21:03:46 +0000 Subject: [PATCH 5/8] Improve test cases Signed-off-by: Babak K. Shandiz --- ...PatternAndAssignment5SiblingInitializer.js | 10 ++++++++++ ...rnAndAssignment5SiblingInitializer.symbols | 15 ++++++++++++++ ...ternAndAssignment5SiblingInitializer.types | 17 ++++++++++++++++ ...PatternAndAssignment9SiblingInitializer.js | 13 +++++++++++- ...rnAndAssignment9SiblingInitializer.symbols | 18 +++++++++++++++++ ...ternAndAssignment9SiblingInitializer.types | 20 +++++++++++++++++++ ...PatternAndAssignment5SiblingInitializer.ts | 7 +++++++ ...PatternAndAssignment9SiblingInitializer.ts | 9 ++++++++- 8 files changed, 107 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js index 58d662d96f1b8..47eaab26cd17b 100644 --- a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js @@ -18,6 +18,13 @@ function f3() { const [a1, b1 = a1] = ['hi', 1]; const [a2, b2 = a2 + '!'] = ['hi', 1]; } + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: [number, number] | undefined +function f4() { + const [ a, b = a ] = yadda ?? []; +} //// [destructuringArrayBindingPatternAndAssignment5SiblingInitializer.js] @@ -36,3 +43,6 @@ function f3() { var _a = ['hi', 1], a1 = _a[0], _b = _a[1], b1 = _b === void 0 ? a1 : _b; var _c = ['hi', 1], a2 = _c[0], _d = _c[1], b2 = _d === void 0 ? a2 + '!' : _d; } +function f4() { + var _a = yadda !== null && yadda !== void 0 ? yadda : [], a = _a[0], _b = _a[1], b = _b === void 0 ? a : _b; +} diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols index 115f9f7f0a6f8..2ff79c20083f2 100644 --- a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.symbols @@ -46,3 +46,18 @@ function f3() { >a2 : Symbol(a2, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 15, 11)) } +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: [number, number] | undefined +>yadda : Symbol(yadda, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 20, 13)) + +function f4() { +>f4 : Symbol(f4, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 20, 49)) + + const [ a, b = a ] = yadda ?? []; +>a : Symbol(a, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 22, 11)) +>b : Symbol(b, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 22, 14)) +>a : Symbol(a, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 22, 11)) +>yadda : Symbol(yadda, Decl(destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts, 20, 13)) +} + diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types index df11e27070dfb..009a62ee2d987 100644 --- a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.types @@ -66,3 +66,20 @@ function f3() { >1 : 1 } +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: [number, number] | undefined +>yadda : [number, number] + +function f4() { +>f4 : () => void + + const [ a, b = a ] = yadda ?? []; +>a : number +>b : number +>a : number +>yadda ?? [] : [number, number] | [] +>yadda : [number, number] +>[] : [] +} + diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js index 7ce61e4b8a1ac..92a7a56842bfc 100644 --- a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js @@ -17,7 +17,15 @@ function f2() { function f3() { const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; -} +} + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: { a?: number, b?: number } | undefined +function f4() { + const { a, b = a } = yadda ?? {}; +} + //// [destructuringObjectBindingPatternAndAssignment9SiblingInitializer.js] // To be inferred as `number` @@ -35,3 +43,6 @@ function f3() { var _a = { a1: 'hi', b1: 1 }, a1 = _a.a1, _b = _a.b1, b1 = _b === void 0 ? a1 : _b; var _c = { a2: 'hi', b2: 1 }, a2 = _c.a2, _d = _c.b2, b2 = _d === void 0 ? a2 + '!' : _d; } +function f4() { + var _a = yadda !== null && yadda !== void 0 ? yadda : {}, a = _a.a, _b = _a.b, b = _b === void 0 ? a : _b; +} diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols index 57eb6eb863156..3c76a5d2b045d 100644 --- a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.symbols @@ -53,3 +53,21 @@ function f3() { >a2 : Symbol(a2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 35)) >b2 : Symbol(b2, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 15, 45)) } + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: { a?: number, b?: number } | undefined +>yadda : Symbol(yadda, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 20, 13)) +>a : Symbol(a, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 20, 22)) +>b : Symbol(b, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 20, 34)) + +function f4() { +>f4 : Symbol(f4, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 20, 59)) + + const { a, b = a } = yadda ?? {}; +>a : Symbol(a, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 22, 11)) +>b : Symbol(b, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 22, 14)) +>a : Symbol(a, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 22, 11)) +>yadda : Symbol(yadda, Decl(destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts, 20, 13)) +} + diff --git a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types index 2b7bc7d2aa1b4..c5ebaabe82453 100644 --- a/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types +++ b/tests/baselines/reference/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.types @@ -73,3 +73,23 @@ function f3() { >b2 : number >1 : 1 } + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: { a?: number, b?: number } | undefined +>yadda : { a?: number; b?: number; } +>a : number +>b : number + +function f4() { +>f4 : () => void + + const { a, b = a } = yadda ?? {}; +>a : number +>b : number +>a : number +>yadda ?? {} : { a?: number; b?: number; } +>yadda : { a?: number; b?: number; } +>{} : {} +} + diff --git a/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts index fecc40937fcd3..6183e22b61fbf 100644 --- a/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts +++ b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment5SiblingInitializer.ts @@ -17,3 +17,10 @@ function f3() { const [a1, b1 = a1] = ['hi', 1]; const [a2, b2 = a2 + '!'] = ['hi', 1]; } + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: [number, number] | undefined +function f4() { + const [ a, b = a ] = yadda ?? []; +} diff --git a/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts index 200e75e336389..d4d5cd7c6cd4d 100644 --- a/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts +++ b/tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment9SiblingInitializer.ts @@ -16,4 +16,11 @@ function f2() { function f3() { const { a1, b1 = a1 } = { a1: 'hi', b1: 1 }; const { a2, b2 = a2 + '!' } = { a2: 'hi', b2: 1 }; -} \ No newline at end of file +} + +// Based on comment: +// - https://github.com/microsoft/TypeScript/issues/49989#issuecomment-1852694486 +declare const yadda: { a?: number, b?: number } | undefined +function f4() { + const { a, b = a } = yadda ?? {}; +} From 86a543b9ad33dff0b4ae7abac3b6c8195b0fd5cb Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Sat, 16 Dec 2023 22:02:30 +0000 Subject: [PATCH 6/8] Restrict ignoring circular error to binding element contextual type checking Signed-off-by: Babak K. Shandiz --- src/compiler/checker.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ab8bd8fb6af0..7be367dc6dd76 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11635,7 +11635,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfFuncClassEnumModule(symbol); } - if (checkMode !== CheckMode.Normal && checkMode) { + // When trying to get the *contextual* type of a binding element, it's possible to fall in a loop and therefore + // end up in a circularity-like situation. This is not a true circularity so we should not report such an error. + // For example, here the looping could happen when trying to get the type of `a` (binding element): + // + // const { a, b = a } = { a: 0 } + // + if (isBindingElement(declaration) && checkMode === CheckMode.Contextual) { return anyType; } return reportCircularityError(symbol); @@ -11711,7 +11717,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfFuncClassEnumModule(symbol); } - if (checkMode !== CheckMode.Normal && checkMode) { + // When trying to get the *contextual* type of a binding element, it's possible to fall in a loop and therefore + // end up in a circularity-like situation. This is not a true circularity so we should not report such an error. + // For example, here the looping could happen when trying to get the type of `a` (binding element): + // + // const { a, b = a } = { a: 0 } + // + if (isBindingElement(declaration) && checkMode === CheckMode.Contextual) { return type; } return reportCircularityError(symbol); From e872af56a8c9139cc17fdad641f6ef07b3e5a290 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Sat, 16 Dec 2023 22:05:49 +0000 Subject: [PATCH 7/8] Return `errorType` when resolving binding element contextual type falls in a loop Signed-off-by: Babak K. Shandiz --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7be367dc6dd76..3dc2dd68f2f1d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11642,7 +11642,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // const { a, b = a } = { a: 0 } // if (isBindingElement(declaration) && checkMode === CheckMode.Contextual) { - return anyType; + return errorType; } return reportCircularityError(symbol); } From 6b7d58033d9292ed41fb37bf47869d11416ecd9b Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Wed, 10 Jan 2024 21:12:42 +0000 Subject: [PATCH 8/8] Simplify condition on `checkMode` Signed-off-by: Babak K. Shandiz --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3dc2dd68f2f1d..c98e5743478ef 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11583,7 +11583,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // to preserve this type. In fact, we need to _prefer_ that type, but it won't // be assigned until contextual typing is complete, so we need to defer in // cases where contextual typing may take place. - if (!links.type && !isParameterOfContextSensitiveSignature(symbol) && !(checkMode !== CheckMode.Normal && checkMode)) { + if (!links.type && !isParameterOfContextSensitiveSignature(symbol) && !checkMode) { links.type = type; } return type;