Skip to content

Commit 756392c

Browse files
authored
support QualifiedName when narrowing inside loops (#43592)
* support QualifiedName when narrowing inside loops * add test * narrow more qualified names * handle `undefined` of `getFlowCacheKey ` * update comments in test
1 parent 14343be commit 756392c

7 files changed

+755
-1
lines changed

src/compiler/binder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2518,7 +2518,7 @@ namespace ts {
25182518
}
25192519
return checkContextualIdentifier(node as Identifier);
25202520
case SyntaxKind.QualifiedName:
2521-
if (currentFlow && parent.kind === SyntaxKind.TypeQuery) {
2521+
if (currentFlow && isPartOfTypeQuery(node)) {
25222522
node.flowNode = currentFlow;
25232523
}
25242524
break;

src/compiler/checker.ts

+3
Original file line numberDiff line numberDiff line change
@@ -21955,6 +21955,9 @@ namespace ts {
2195521955
case SyntaxKind.NonNullExpression:
2195621956
case SyntaxKind.ParenthesizedExpression:
2195721957
return getFlowCacheKey((node as NonNullExpression | ParenthesizedExpression).expression, declaredType, initialType, flowContainer);
21958+
case SyntaxKind.QualifiedName:
21959+
const left = getFlowCacheKey((node as QualifiedName).left, declaredType, initialType, flowContainer);
21960+
return left && left + "." + (node as QualifiedName).right.escapedText;
2195821961
case SyntaxKind.PropertyAccessExpression:
2195921962
case SyntaxKind.ElementAccessExpression:
2196021963
const propName = getAccessedPropertyName(node as AccessExpression);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
tests/cases/compiler/narrowingOfQualifiedNames.ts(33,25): error TS2532: Object is possibly 'undefined'.
2+
tests/cases/compiler/narrowingOfQualifiedNames.ts(38,29): error TS2532: Object is possibly 'undefined'.
3+
4+
5+
==== tests/cases/compiler/narrowingOfQualifiedNames.ts (2 errors) ====
6+
// Repro from #43411
7+
8+
interface IProperties {
9+
foo?: {
10+
aaa: string
11+
bbb: string
12+
}
13+
}
14+
15+
function init(properties: IProperties) {
16+
if (properties.foo) {
17+
type FooOK = typeof properties.foo;
18+
properties.foo; // type is { aaa: string; bbb: string; }
19+
for (const x of [1, 2, 3]) {
20+
properties.foo; // type is { aaa: string; bbb: string; }
21+
type FooOrUndefined = typeof properties.foo; // type should be { aaa: string; bbb: string; }
22+
}
23+
}
24+
}
25+
26+
interface DeepOptional {
27+
a?: {
28+
b?: {
29+
c?: string
30+
}
31+
}
32+
}
33+
34+
function init2(foo: DeepOptional) {
35+
if (foo.a) {
36+
type A = typeof foo.a;
37+
type B = typeof foo.a.b;
38+
type C = typeof foo.a.b.c;
39+
~~~~~~~
40+
!!! error TS2532: Object is possibly 'undefined'.
41+
42+
for(const _ of [1]) {
43+
type A = typeof foo.a;
44+
type B = typeof foo.a.b;
45+
type C = typeof foo.a.b.c;
46+
~~~~~~~
47+
!!! error TS2532: Object is possibly 'undefined'.
48+
49+
if (foo.a.b) {
50+
type A = typeof foo.a;
51+
type B = typeof foo.a.b;
52+
type C = typeof foo.a.b.c;
53+
54+
for(const _ of [1]) {
55+
type A = typeof foo.a;
56+
type B = typeof foo.a.b;
57+
type C = typeof foo.a.b.c;
58+
59+
if (foo.a.b.c) {
60+
type A = typeof foo.a;
61+
type B = typeof foo.a.b;
62+
type C = typeof foo.a.b.c;
63+
64+
for(const _ of [1]) {
65+
type A = typeof foo.a;
66+
type B = typeof foo.a.b;
67+
type C = typeof foo.a.b.c;
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//// [narrowingOfQualifiedNames.ts]
2+
// Repro from #43411
3+
4+
interface IProperties {
5+
foo?: {
6+
aaa: string
7+
bbb: string
8+
}
9+
}
10+
11+
function init(properties: IProperties) {
12+
if (properties.foo) {
13+
type FooOK = typeof properties.foo;
14+
properties.foo; // type is { aaa: string; bbb: string; }
15+
for (const x of [1, 2, 3]) {
16+
properties.foo; // type is { aaa: string; bbb: string; }
17+
type FooOrUndefined = typeof properties.foo; // type should be { aaa: string; bbb: string; }
18+
}
19+
}
20+
}
21+
22+
interface DeepOptional {
23+
a?: {
24+
b?: {
25+
c?: string
26+
}
27+
}
28+
}
29+
30+
function init2(foo: DeepOptional) {
31+
if (foo.a) {
32+
type A = typeof foo.a;
33+
type B = typeof foo.a.b;
34+
type C = typeof foo.a.b.c;
35+
36+
for(const _ of [1]) {
37+
type A = typeof foo.a;
38+
type B = typeof foo.a.b;
39+
type C = typeof foo.a.b.c;
40+
41+
if (foo.a.b) {
42+
type A = typeof foo.a;
43+
type B = typeof foo.a.b;
44+
type C = typeof foo.a.b.c;
45+
46+
for(const _ of [1]) {
47+
type A = typeof foo.a;
48+
type B = typeof foo.a.b;
49+
type C = typeof foo.a.b.c;
50+
51+
if (foo.a.b.c) {
52+
type A = typeof foo.a;
53+
type B = typeof foo.a.b;
54+
type C = typeof foo.a.b.c;
55+
56+
for(const _ of [1]) {
57+
type A = typeof foo.a;
58+
type B = typeof foo.a.b;
59+
type C = typeof foo.a.b.c;
60+
}
61+
}
62+
}
63+
}
64+
}
65+
}
66+
}
67+
68+
//// [narrowingOfQualifiedNames.js]
69+
"use strict";
70+
// Repro from #43411
71+
function init(properties) {
72+
if (properties.foo) {
73+
properties.foo; // type is { aaa: string; bbb: string; }
74+
for (var _i = 0, _a = [1, 2, 3]; _i < _a.length; _i++) {
75+
var x = _a[_i];
76+
properties.foo; // type is { aaa: string; bbb: string; }
77+
}
78+
}
79+
}
80+
function init2(foo) {
81+
if (foo.a) {
82+
for (var _i = 0, _a = [1]; _i < _a.length; _i++) {
83+
var _ = _a[_i];
84+
if (foo.a.b) {
85+
for (var _b = 0, _c = [1]; _b < _c.length; _b++) {
86+
var _1 = _c[_b];
87+
if (foo.a.b.c) {
88+
for (var _d = 0, _e = [1]; _d < _e.length; _d++) {
89+
var _2 = _e[_d];
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)