From aed5fc0b03f2356c8f3d0bf660f9005e5b18b6d4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Mar 2024 21:17:12 -0700 Subject: [PATCH 1/6] CFA for obj[key] where key is constant variable or parameter --- src/compiler/checker.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c8eefa77783f3..e59343db9b7d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26543,13 +26543,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getFlowCacheKey((node as NonNullExpression | ParenthesizedExpression).expression, declaredType, initialType, flowContainer); case SyntaxKind.QualifiedName: const left = getFlowCacheKey((node as QualifiedName).left, declaredType, initialType, flowContainer); - return left && left + "." + (node as QualifiedName).right.escapedText; + return left && `${left}.${(node as QualifiedName).right.escapedText}`; case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: const propName = getAccessedPropertyName(node as AccessExpression); if (propName !== undefined) { const key = getFlowCacheKey((node as AccessExpression).expression, declaredType, initialType, flowContainer); - return key && key + "." + propName; + return key && `${key}.${propName}`; + } + if (isElementAccessExpression(node) && isIdentifier(node.argumentExpression)) { + const symbol = getResolvedSymbol(node.argumentExpression); + if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) { + const key = getFlowCacheKey((node as AccessExpression).expression, declaredType, initialType, flowContainer); + return key && `${key}.@${getSymbolId(symbol)}`; + } } break; case SyntaxKind.ObjectBindingPattern: @@ -26595,9 +26602,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: const sourcePropertyName = getAccessedPropertyName(source as AccessExpression); - const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined; - return sourcePropertyName !== undefined && targetPropertyName !== undefined && targetPropertyName === sourcePropertyName && - isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression); + if (sourcePropertyName !== undefined) { + const targetPropertyName = isAccessExpression(target) ? getAccessedPropertyName(target) : undefined; + if (targetPropertyName !== undefined) { + return targetPropertyName === sourcePropertyName && isMatchingReference((source as AccessExpression).expression, (target as AccessExpression).expression); + } + } + if (isElementAccessExpression(source) && isElementAccessExpression(target) && isIdentifier(source.argumentExpression) && isIdentifier(target.argumentExpression)) { + const symbol = getResolvedSymbol(source.argumentExpression); + if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) { + return symbol === getResolvedSymbol(target.argumentExpression); + } + } + break; case SyntaxKind.QualifiedName: return isAccessExpression(target) && (source as QualifiedName).right.escapedText === getAccessedPropertyName(target) && @@ -26636,7 +26653,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function tryGetNameFromEntityNameExpression(node: EntityNameOrEntityNameExpression) { - const symbol = resolveEntityName(node, SymbolFlags.Value, /*ignoreErrors*/ true); + const symbol = isIdentifier(node) ? getResolvedSymbol(node) : resolveEntityName(node, SymbolFlags.Value, /*ignoreErrors*/ true); if (!symbol || !(isConstantVariable(symbol) || (symbol.flags & SymbolFlags.EnumMember))) return undefined; const declaration = symbol.valueDeclaration; From 8676121e2d61c4b7934d732654e17b5007ede141 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Mar 2024 21:17:30 -0700 Subject: [PATCH 2/6] Accept new baselines --- .../reference/mappedTypeGenericIndexedAccess.types | 8 ++++---- .../reference/noUncheckedIndexedAccess.errors.txt | 6 ++---- tests/baselines/reference/noUncheckedIndexedAccess.types | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/mappedTypeGenericIndexedAccess.types b/tests/baselines/reference/mappedTypeGenericIndexedAccess.types index 75dd61b90a26e..cb4107f6d38a3 100644 --- a/tests/baselines/reference/mappedTypeGenericIndexedAccess.types +++ b/tests/baselines/reference/mappedTypeGenericIndexedAccess.types @@ -60,14 +60,14 @@ class Test { >[] : never[] } this.entries[name]?.push(entry); ->this.entries[name]?.push(entry) : number | undefined ->this.entries[name]?.push : ((...items: Types[T][]) => number) | undefined ->this.entries[name] : Types[T][] | undefined +>this.entries[name]?.push(entry) : number +>this.entries[name]?.push : (...items: Types[T][]) => number +>this.entries[name] : Types[T][] >this.entries : { first?: { a1: true; }[] | undefined; second?: { a2: true; }[] | undefined; third?: { a3: true; }[] | undefined; } >this : this >entries : { first?: { a1: true; }[] | undefined; second?: { a2: true; }[] | undefined; third?: { a3: true; }[] | undefined; } >name : T ->push : ((...items: Types[T][]) => number) | undefined +>push : (...items: Types[T][]) => number >entry : Types[T] } } diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt b/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt index 5e3da68cf77f1..1199c0dbb17d1 100644 --- a/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt +++ b/tests/baselines/reference/noUncheckedIndexedAccess.errors.txt @@ -33,8 +33,7 @@ noUncheckedIndexedAccess.ts(90,7): error TS2322: Type 'string | undefined' is no Type 'undefined' is not assignable to type 'string'. noUncheckedIndexedAccess.ts(98,5): error TS2322: Type 'undefined' is not assignable to type '{ [key: string]: string; a: string; b: string; }[Key]'. Type 'undefined' is not assignable to type 'string'. -noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'string | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. +noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'undefined' is not assignable to type 'string'. ==== noUncheckedIndexedAccess.ts (31 errors) ==== @@ -203,8 +202,7 @@ noUncheckedIndexedAccess.ts(99,11): error TS2322: Type 'string | undefined' is n !!! error TS2322: Type 'undefined' is not assignable to type 'string'. const v: string = myRecord2[key]; // Should error ~ -!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'. -!!! error TS2322: Type 'undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. }; \ No newline at end of file diff --git a/tests/baselines/reference/noUncheckedIndexedAccess.types b/tests/baselines/reference/noUncheckedIndexedAccess.types index 54fad592dfc87..c6fffb01a5f50 100644 --- a/tests/baselines/reference/noUncheckedIndexedAccess.types +++ b/tests/baselines/reference/noUncheckedIndexedAccess.types @@ -412,7 +412,7 @@ const fn3 = (key: Key) => { const v: string = myRecord2[key]; // Should error >v : string ->myRecord2[key] : string | undefined +>myRecord2[key] : undefined >myRecord2 : { [key: string]: string; a: string; b: string; } >key : Key From 2ce7c15fa8a3158186d21417cb003074eb3d8a70 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Mar 2024 07:04:35 -0700 Subject: [PATCH 3/6] Add tests --- .../controlFlowComputedPropertyNames.symbols | 134 ++++++++++++++ .../controlFlowComputedPropertyNames.types | 163 ++++++++++++++++++ .../controlFlowComputedPropertyNames.ts | 41 +++++ 3 files changed, 338 insertions(+) create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames.symbols create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames.symbols b/tests/baselines/reference/controlFlowComputedPropertyNames.symbols new file mode 100644 index 0000000000000..2d3236aff4621 --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames.symbols @@ -0,0 +1,134 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts] //// + +=== controlFlowComputedPropertyNames.ts === +function f1(obj: Record, key: string) { +>f1 : Symbol(f1, Decl(controlFlowComputedPropertyNames.ts, 0, 0)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41)) + + if (typeof obj[key] === "string") { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41)) + + obj[key].toUpperCase(); +>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 0, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 0, 41)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } +} + +function f2(obj: Record, key: string) { +>f2 : Symbol(f2, Decl(controlFlowComputedPropertyNames.ts, 4, 1)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) + + if (obj[key] !== undefined) { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) +>undefined : Symbol(undefined) + + obj[key].toUpperCase(); +>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } + let key2 = key + key; +>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) + + if (obj[key2] !== undefined) { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7)) +>undefined : Symbol(undefined) + + obj[key2].toUpperCase(); +>obj[key2].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key2 : Symbol(key2, Decl(controlFlowComputedPropertyNames.ts, 10, 7)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } + const key3 = key + key; +>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 6, 52)) + + if (obj[key3] !== undefined) { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9)) +>undefined : Symbol(undefined) + + obj[key3].toUpperCase(); +>obj[key3].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 6, 12)) +>key3 : Symbol(key3, Decl(controlFlowComputedPropertyNames.ts, 14, 9)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } +} + +type Thing = { a?: string, b?: number, c?: number }; +>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1)) +>a : Symbol(a, Decl(controlFlowComputedPropertyNames.ts, 20, 14)) +>b : Symbol(b, Decl(controlFlowComputedPropertyNames.ts, 20, 26)) +>c : Symbol(c, Decl(controlFlowComputedPropertyNames.ts, 20, 38)) + +function f3(obj: Thing, key: keyof Thing) { +>f3 : Symbol(f3, Decl(controlFlowComputedPropertyNames.ts, 20, 52)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) +>Thing : Symbol(Thing, Decl(controlFlowComputedPropertyNames.ts, 18, 1)) + + if (obj[key] !== undefined) { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) +>undefined : Symbol(undefined) + + if (typeof obj[key] === "string") { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) + + obj[key].toUpperCase(); +>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } + if (typeof obj[key] === "number") { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) + + obj[key].toFixed(); +>obj[key].toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 22, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 22, 23)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + } + } +} + +function f4(obj: Record, key: K) { +>f4 : Symbol(f4, Decl(controlFlowComputedPropertyNames.ts, 31, 1)) +>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65)) +>K : Symbol(K, Decl(controlFlowComputedPropertyNames.ts, 33, 12)) + + if (obj[key]) { +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65)) + + obj[key].toUpperCase(); +>obj[key].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(controlFlowComputedPropertyNames.ts, 33, 30)) +>key : Symbol(key, Decl(controlFlowComputedPropertyNames.ts, 33, 65)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames.types b/tests/baselines/reference/controlFlowComputedPropertyNames.types new file mode 100644 index 0000000000000..6fd96872fa7f3 --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames.types @@ -0,0 +1,163 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts] //// + +=== controlFlowComputedPropertyNames.ts === +function f1(obj: Record, key: string) { +>f1 : (obj: Record, key: string) => void +>obj : Record +>key : string + + if (typeof obj[key] === "string") { +>typeof obj[key] === "string" : boolean +>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>obj[key] : unknown +>obj : Record +>key : string +>"string" : "string" + + obj[key].toUpperCase(); +>obj[key].toUpperCase() : string +>obj[key].toUpperCase : () => string +>obj[key] : string +>obj : Record +>key : string +>toUpperCase : () => string + } +} + +function f2(obj: Record, key: string) { +>f2 : (obj: Record, key: string) => void +>obj : Record +>key : string + + if (obj[key] !== undefined) { +>obj[key] !== undefined : boolean +>obj[key] : string | undefined +>obj : Record +>key : string +>undefined : undefined + + obj[key].toUpperCase(); +>obj[key].toUpperCase() : string +>obj[key].toUpperCase : () => string +>obj[key] : string +>obj : Record +>key : string +>toUpperCase : () => string + } + let key2 = key + key; +>key2 : string +>key + key : string +>key : string +>key : string + + if (obj[key2] !== undefined) { +>obj[key2] !== undefined : boolean +>obj[key2] : string | undefined +>obj : Record +>key2 : string +>undefined : undefined + + obj[key2].toUpperCase(); +>obj[key2].toUpperCase() : string +>obj[key2].toUpperCase : () => string +>obj[key2] : string +>obj : Record +>key2 : string +>toUpperCase : () => string + } + const key3 = key + key; +>key3 : string +>key + key : string +>key : string +>key : string + + if (obj[key3] !== undefined) { +>obj[key3] !== undefined : boolean +>obj[key3] : string | undefined +>obj : Record +>key3 : string +>undefined : undefined + + obj[key3].toUpperCase(); +>obj[key3].toUpperCase() : string +>obj[key3].toUpperCase : () => string +>obj[key3] : string +>obj : Record +>key3 : string +>toUpperCase : () => string + } +} + +type Thing = { a?: string, b?: number, c?: number }; +>Thing : { a?: string | undefined; b?: number | undefined; c?: number | undefined; } +>a : string | undefined +>b : number | undefined +>c : number | undefined + +function f3(obj: Thing, key: keyof Thing) { +>f3 : (obj: Thing, key: keyof Thing) => void +>obj : Thing +>key : keyof Thing + + if (obj[key] !== undefined) { +>obj[key] !== undefined : boolean +>obj[key] : string | number | undefined +>obj : Thing +>key : keyof Thing +>undefined : undefined + + if (typeof obj[key] === "string") { +>typeof obj[key] === "string" : boolean +>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>obj[key] : string | number +>obj : Thing +>key : keyof Thing +>"string" : "string" + + obj[key].toUpperCase(); +>obj[key].toUpperCase() : string +>obj[key].toUpperCase : () => string +>obj[key] : string +>obj : Thing +>key : keyof Thing +>toUpperCase : () => string + } + if (typeof obj[key] === "number") { +>typeof obj[key] === "number" : boolean +>typeof obj[key] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>obj[key] : string | number +>obj : Thing +>key : keyof Thing +>"number" : "number" + + obj[key].toFixed(); +>obj[key].toFixed() : string +>obj[key].toFixed : (fractionDigits?: number | undefined) => string +>obj[key] : number +>obj : Thing +>key : keyof Thing +>toFixed : (fractionDigits?: number | undefined) => string + } + } +} + +function f4(obj: Record, key: K) { +>f4 : (obj: Record, key: K) => void +>obj : Record +>key : K + + if (obj[key]) { +>obj[key] : Record[K] +>obj : Record +>key : K + + obj[key].toUpperCase(); +>obj[key].toUpperCase() : string +>obj[key].toUpperCase : () => string +>obj[key] : string +>obj : Record +>key : K +>toUpperCase : () => string + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts new file mode 100644 index 0000000000000..5c29e86f97d3f --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames.ts @@ -0,0 +1,41 @@ +// @strict: true +// @noEmit: true + +function f1(obj: Record, key: string) { + if (typeof obj[key] === "string") { + obj[key].toUpperCase(); + } +} + +function f2(obj: Record, key: string) { + if (obj[key] !== undefined) { + obj[key].toUpperCase(); + } + let key2 = key + key; + if (obj[key2] !== undefined) { + obj[key2].toUpperCase(); + } + const key3 = key + key; + if (obj[key3] !== undefined) { + obj[key3].toUpperCase(); + } +} + +type Thing = { a?: string, b?: number, c?: number }; + +function f3(obj: Thing, key: keyof Thing) { + if (obj[key] !== undefined) { + if (typeof obj[key] === "string") { + obj[key].toUpperCase(); + } + if (typeof obj[key] === "number") { + obj[key].toFixed(); + } + } +} + +function f4(obj: Record, key: K) { + if (obj[key]) { + obj[key].toUpperCase(); + } +} From 95818c0ad0eba85ca56c71963874162a47aaeba3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Mar 2024 10:30:26 -0700 Subject: [PATCH 4/6] Fully compare references --- 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 e59343db9b7d7..ed7ce03732160 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26611,7 +26611,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isElementAccessExpression(source) && isElementAccessExpression(target) && isIdentifier(source.argumentExpression) && isIdentifier(target.argumentExpression)) { const symbol = getResolvedSymbol(source.argumentExpression); if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) { - return symbol === getResolvedSymbol(target.argumentExpression); + return symbol === getResolvedSymbol(target.argumentExpression) && isMatchingReference(source.expression, target.expression); } } break; From aa68fd87560090a2817cbb09fa4e3079aedfd422 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Mar 2024 11:35:04 -0700 Subject: [PATCH 5/6] Minor optimization --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ed7ce03732160..afc30526c31fd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26610,8 +26610,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isElementAccessExpression(source) && isElementAccessExpression(target) && isIdentifier(source.argumentExpression) && isIdentifier(target.argumentExpression)) { const symbol = getResolvedSymbol(source.argumentExpression); - if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) { - return symbol === getResolvedSymbol(target.argumentExpression) && isMatchingReference(source.expression, target.expression); + if (symbol === getResolvedSymbol(target.argumentExpression) && (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol))) { + return isMatchingReference(source.expression, target.expression); } } break; From 8a277ec61c89f68beb4a9d08c93e36363e612824 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Mar 2024 13:25:33 -0700 Subject: [PATCH 6/6] Restore original resolveEntityName call --- 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 afc30526c31fd..ee53c16fe8ec5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26653,7 +26653,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function tryGetNameFromEntityNameExpression(node: EntityNameOrEntityNameExpression) { - const symbol = isIdentifier(node) ? getResolvedSymbol(node) : resolveEntityName(node, SymbolFlags.Value, /*ignoreErrors*/ true); + const symbol = resolveEntityName(node, SymbolFlags.Value, /*ignoreErrors*/ true); if (!symbol || !(isConstantVariable(symbol) || (symbol.flags & SymbolFlags.EnumMember))) return undefined; const declaration = symbol.valueDeclaration;