diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d4f288f1293b..ac8fc8e275461 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2834,8 +2834,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) || declaration.pos < errorBindingElement.pos; } + const rootDeclaration = getRootDeclaration(declaration) as Declaration; + if (rootDeclaration.kind !== SyntaxKind.VariableDeclaration) { + return true; + } // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) - return isBlockScopedNameDeclaredBeforeUse(getAncestor(declaration, SyntaxKind.VariableDeclaration) as Declaration, usage); + return isBlockScopedNameDeclaredBeforeUse(rootDeclaration, usage); } else if (declaration.kind === SyntaxKind.VariableDeclaration) { // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) @@ -26404,29 +26408,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function tryGetNameFromEntityNameExpression(node: EntityNameOrEntityNameExpression) { const symbol = resolveEntityName(node, SymbolFlags.Value, /*ignoreErrors*/ true); - if (!symbol || !(isConstantVariable(symbol) || (symbol.flags & SymbolFlags.EnumMember))) return undefined; - - const declaration = symbol.valueDeclaration; - if (declaration === undefined) return undefined; - - const type = tryGetTypeFromEffectiveTypeNode(declaration); - if (type) { - const name = tryGetNameFromType(type); - if (name !== undefined) { - return name; - } - } - if (hasOnlyExpressionInitializer(declaration) && isBlockScopedNameDeclaredBeforeUse(declaration, node)) { - const initializer = getEffectiveInitializer(declaration); - if (initializer) { - const initializerType = isBindingPattern(declaration.parent) ? getTypeForBindingElement(declaration as BindingElement) : getTypeOfExpression(initializer); - return initializerType && tryGetNameFromType(initializerType); - } - if (isEnumMember(declaration)) { - return getTextOfPropertyName(declaration.name); - } - } - return undefined; + if (!symbol || symbol.valueDeclaration && !isBlockScopedNameDeclaredBeforeUse(symbol.valueDeclaration, node)) return undefined; + return tryGetNameFromType(getTypeOfExpression(node as Expression)); } function containsMatchingReference(source: Node, target: Node) { diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.symbols b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.symbols new file mode 100644 index 0000000000000..7375a1e4f3869 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.symbols @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty13.ts] //// + +=== typeGuardNarrowsIndexedAccessOfKnownProperty13.ts === +interface Data { +>Data : Symbol(Data, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 0, 0)) + + a?: number; +>a : Symbol(Data.a, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 0, 16)) +} + +declare const data: Data; +>data : Symbol(data, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 4, 13)) +>Data : Symbol(Data, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 0, 0)) + +let key = "a" as const; +>key : Symbol(key, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 6, 3)) +>const : Symbol(const) + +if (data.a !== undefined) { +>data.a : Symbol(Data.a, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 0, 16)) +>data : Symbol(data, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 4, 13)) +>a : Symbol(Data.a, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 0, 16)) +>undefined : Symbol(undefined) + + const a = data[key]; +>a : Symbol(a, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 9, 7)) +>data : Symbol(data, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 4, 13)) +>key : Symbol(key, Decl(typeGuardNarrowsIndexedAccessOfKnownProperty13.ts, 6, 3)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.types b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.types new file mode 100644 index 0000000000000..76d050d2b89f3 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsIndexedAccessOfKnownProperty13.types @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty13.ts] //// + +=== typeGuardNarrowsIndexedAccessOfKnownProperty13.ts === +interface Data { + a?: number; +>a : number | undefined +} + +declare const data: Data; +>data : Data + +let key = "a" as const; +>key : "a" +>"a" as const : "a" +>"a" : "a" + +if (data.a !== undefined) { +>data.a !== undefined : boolean +>data.a : number | undefined +>data : Data +>a : number | undefined +>undefined : undefined + + const a = data[key]; +>a : number +>data[key] : number +>data : Data +>key : "a" +} + diff --git a/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty13.ts b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty13.ts new file mode 100644 index 0000000000000..2ed928b758ba2 --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowsIndexedAccessOfKnownProperty13.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +interface Data { + a?: number; +} + +declare const data: Data; + +let key = "a" as const; + +if (data.a !== undefined) { + const a = data[key]; +}