diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b397bbf28d5..365a3d36515 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2540,6 +2540,11 @@ namespace ts { node.flowNode = currentFlow; } return checkContextualIdentifier(node); + case SyntaxKind.QualifiedName: + if (currentFlow && parent.kind === SyntaxKind.TypeQuery) { + node.flowNode = currentFlow; + } + break; case SyntaxKind.SuperKeyword: node.flowNode = currentFlow; break; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6903d7ef1b4..4293cd82cb7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21289,8 +21289,12 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: return isAccessExpression(target) && - getAccessedPropertyName(source) === getAccessedPropertyName(target) && - isMatchingReference((source).expression, target.expression); + getAccessedPropertyName(source as AccessExpression) === getAccessedPropertyName(target) && + isMatchingReference((source as AccessExpression).expression, target.expression); + case SyntaxKind.QualifiedName: + return isAccessExpression(target) && + (source as QualifiedName).right.escapedText === getAccessedPropertyName(target) && + isMatchingReference((source as QualifiedName).left, target.expression); case SyntaxKind.BinaryExpression: return (isBinaryExpression(source) && source.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source.right, target)); } @@ -26421,8 +26425,7 @@ namespace ts { // assignment target, and the referenced property was declared as a variable, property, // accessor, or optional method. const assignmentKind = getAssignmentTargetKind(node); - if (!isAccessExpression(node) || - assignmentKind === AssignmentKind.Definite || + if (assignmentKind === AssignmentKind.Definite || prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { return propType; } @@ -26434,7 +26437,7 @@ namespace ts { // and if we are in a constructor of the same class as the property declaration, assume that // the property is uninitialized at the top of the control flow. let assumeUninitialized = false; - if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) { + if (strictNullChecks && strictPropertyInitialization && isAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword) { const declaration = prop && prop.valueDeclaration; if (declaration && isInstancePropertyWithoutInitializer(declaration)) { const flowContainer = getControlFlowContainer(node); diff --git a/tests/baselines/reference/controlFlowIfStatement.js b/tests/baselines/reference/controlFlowIfStatement.js index e40b831552f..89b1bc80415 100644 --- a/tests/baselines/reference/controlFlowIfStatement.js +++ b/tests/baselines/reference/controlFlowIfStatement.js @@ -51,6 +51,15 @@ function d(data: string | T): never { return data; } } + +interface I { + p: T; +} +function e(x: I<"A" | "B">) { + if (x.p === "A") { + let a: "A" = (null as unknown as typeof x.p) + } +} //// [controlFlowIfStatement.js] @@ -104,3 +113,8 @@ function d(data) { return data; } } +function e(x) { + if (x.p === "A") { + var a_1 = null; + } +} diff --git a/tests/baselines/reference/controlFlowIfStatement.symbols b/tests/baselines/reference/controlFlowIfStatement.symbols index 01d62d4d178..4f95e9aea87 100644 --- a/tests/baselines/reference/controlFlowIfStatement.symbols +++ b/tests/baselines/reference/controlFlowIfStatement.symbols @@ -110,3 +110,29 @@ function d(data: string | T): never { } } +interface I { +>I : Symbol(I, Decl(controlFlowIfStatement.ts, 51, 1)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 53, 12)) + + p: T; +>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 53, 12)) +} +function e(x: I<"A" | "B">) { +>e : Symbol(e, Decl(controlFlowIfStatement.ts, 55, 1)) +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11)) +>I : Symbol(I, Decl(controlFlowIfStatement.ts, 51, 1)) + + if (x.p === "A") { +>x.p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16)) +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11)) +>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16)) + + let a: "A" = (null as unknown as typeof x.p) +>a : Symbol(a, Decl(controlFlowIfStatement.ts, 58, 11)) +>x.p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16)) +>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11)) +>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16)) + } +} + diff --git a/tests/baselines/reference/controlFlowIfStatement.types b/tests/baselines/reference/controlFlowIfStatement.types index 85b04082504..d549ef6f0a2 100644 --- a/tests/baselines/reference/controlFlowIfStatement.types +++ b/tests/baselines/reference/controlFlowIfStatement.types @@ -132,3 +132,30 @@ function d(data: string | T): never { } } +interface I { + p: T; +>p : T +} +function e(x: I<"A" | "B">) { +>e : (x: I<"A" | "B">) => void +>x : I<"A" | "B"> + + if (x.p === "A") { +>x.p === "A" : boolean +>x.p : "A" | "B" +>x : I<"A" | "B"> +>p : "A" | "B" +>"A" : "A" + + let a: "A" = (null as unknown as typeof x.p) +>a : "A" +>(null as unknown as typeof x.p) : "A" +>null as unknown as typeof x.p : "A" +>null as unknown : unknown +>null : null +>x.p : "A" +>x : I<"A" | "B"> +>p : "A" + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts index bd2e606d13f..93a18b4f141 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts @@ -52,3 +52,12 @@ function d(data: string | T): never { return data; } } + +interface I { + p: T; +} +function e(x: I<"A" | "B">) { + if (x.p === "A") { + let a: "A" = (null as unknown as typeof x.p) + } +}