Skip to content

Commit 9dbfaee

Browse files
authored
Narrow QualifiedName inside typeof (microsoft#42540)
Previously this wasn't narrowed at all. Now there is control flow and code in isMatchingReference.
1 parent 5a21291 commit 9dbfaee

File tree

6 files changed

+89
-5
lines changed

6 files changed

+89
-5
lines changed

src/compiler/binder.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,11 @@ namespace ts {
25402540
node.flowNode = currentFlow;
25412541
}
25422542
return checkContextualIdentifier(<Identifier>node);
2543+
case SyntaxKind.QualifiedName:
2544+
if (currentFlow && parent.kind === SyntaxKind.TypeQuery) {
2545+
node.flowNode = currentFlow;
2546+
}
2547+
break;
25432548
case SyntaxKind.SuperKeyword:
25442549
node.flowNode = currentFlow;
25452550
break;

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21289,8 +21289,12 @@ namespace ts {
2128921289
case SyntaxKind.PropertyAccessExpression:
2129021290
case SyntaxKind.ElementAccessExpression:
2129121291
return isAccessExpression(target) &&
21292-
getAccessedPropertyName(<AccessExpression>source) === getAccessedPropertyName(target) &&
21293-
isMatchingReference((<AccessExpression>source).expression, target.expression);
21292+
getAccessedPropertyName(source as AccessExpression) === getAccessedPropertyName(target) &&
21293+
isMatchingReference((source as AccessExpression).expression, target.expression);
21294+
case SyntaxKind.QualifiedName:
21295+
return isAccessExpression(target) &&
21296+
(source as QualifiedName).right.escapedText === getAccessedPropertyName(target) &&
21297+
isMatchingReference((source as QualifiedName).left, target.expression);
2129421298
case SyntaxKind.BinaryExpression:
2129521299
return (isBinaryExpression(source) && source.operatorToken.kind === SyntaxKind.CommaToken && isMatchingReference(source.right, target));
2129621300
}
@@ -26421,8 +26425,7 @@ namespace ts {
2642126425
// assignment target, and the referenced property was declared as a variable, property,
2642226426
// accessor, or optional method.
2642326427
const assignmentKind = getAssignmentTargetKind(node);
26424-
if (!isAccessExpression(node) ||
26425-
assignmentKind === AssignmentKind.Definite ||
26428+
if (assignmentKind === AssignmentKind.Definite ||
2642626429
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
2642726430
return propType;
2642826431
}
@@ -26434,7 +26437,7 @@ namespace ts {
2643426437
// and if we are in a constructor of the same class as the property declaration, assume that
2643526438
// the property is uninitialized at the top of the control flow.
2643626439
let assumeUninitialized = false;
26437-
if (strictNullChecks && strictPropertyInitialization && node.expression.kind === SyntaxKind.ThisKeyword) {
26440+
if (strictNullChecks && strictPropertyInitialization && isAccessExpression(node) && node.expression.kind === SyntaxKind.ThisKeyword) {
2643826441
const declaration = prop && prop.valueDeclaration;
2643926442
if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
2644026443
const flowContainer = getControlFlowContainer(node);

tests/baselines/reference/controlFlowIfStatement.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ function d<T extends string>(data: string | T): never {
5151
return data;
5252
}
5353
}
54+
55+
interface I<T> {
56+
p: T;
57+
}
58+
function e(x: I<"A" | "B">) {
59+
if (x.p === "A") {
60+
let a: "A" = (null as unknown as typeof x.p)
61+
}
62+
}
5463

5564

5665
//// [controlFlowIfStatement.js]
@@ -104,3 +113,8 @@ function d(data) {
104113
return data;
105114
}
106115
}
116+
function e(x) {
117+
if (x.p === "A") {
118+
var a_1 = null;
119+
}
120+
}

tests/baselines/reference/controlFlowIfStatement.symbols

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,29 @@ function d<T extends string>(data: string | T): never {
110110
}
111111
}
112112

113+
interface I<T> {
114+
>I : Symbol(I, Decl(controlFlowIfStatement.ts, 51, 1))
115+
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 53, 12))
116+
117+
p: T;
118+
>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16))
119+
>T : Symbol(T, Decl(controlFlowIfStatement.ts, 53, 12))
120+
}
121+
function e(x: I<"A" | "B">) {
122+
>e : Symbol(e, Decl(controlFlowIfStatement.ts, 55, 1))
123+
>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11))
124+
>I : Symbol(I, Decl(controlFlowIfStatement.ts, 51, 1))
125+
126+
if (x.p === "A") {
127+
>x.p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16))
128+
>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11))
129+
>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16))
130+
131+
let a: "A" = (null as unknown as typeof x.p)
132+
>a : Symbol(a, Decl(controlFlowIfStatement.ts, 58, 11))
133+
>x.p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16))
134+
>x : Symbol(x, Decl(controlFlowIfStatement.ts, 56, 11))
135+
>p : Symbol(I.p, Decl(controlFlowIfStatement.ts, 53, 16))
136+
}
137+
}
138+

tests/baselines/reference/controlFlowIfStatement.types

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,30 @@ function d<T extends string>(data: string | T): never {
132132
}
133133
}
134134

135+
interface I<T> {
136+
p: T;
137+
>p : T
138+
}
139+
function e(x: I<"A" | "B">) {
140+
>e : (x: I<"A" | "B">) => void
141+
>x : I<"A" | "B">
142+
143+
if (x.p === "A") {
144+
>x.p === "A" : boolean
145+
>x.p : "A" | "B"
146+
>x : I<"A" | "B">
147+
>p : "A" | "B"
148+
>"A" : "A"
149+
150+
let a: "A" = (null as unknown as typeof x.p)
151+
>a : "A"
152+
>(null as unknown as typeof x.p) : "A"
153+
>null as unknown as typeof x.p : "A"
154+
>null as unknown : unknown
155+
>null : null
156+
>x.p : "A"
157+
>x : I<"A" | "B">
158+
>p : "A"
159+
}
160+
}
161+

tests/cases/conformance/controlFlow/controlFlowIfStatement.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,12 @@ function d<T extends string>(data: string | T): never {
5252
return data;
5353
}
5454
}
55+
56+
interface I<T> {
57+
p: T;
58+
}
59+
function e(x: I<"A" | "B">) {
60+
if (x.p === "A") {
61+
let a: "A" = (null as unknown as typeof x.p)
62+
}
63+
}

0 commit comments

Comments
 (0)