Skip to content

Commit c225163

Browse files
committed
Grab the left type when narrowing by in operator lazily
1 parent 218d380 commit c225163

File tree

5 files changed

+347
-9
lines changed

5 files changed

+347
-9
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27167,15 +27167,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2716727167
return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue);
2716827168
}
2716927169
const target = getReferenceCandidate(expr.right);
27170-
const leftType = getTypeOfExpression(expr.left);
27171-
if (leftType.flags & TypeFlags.StringOrNumberLiteralOrUnique) {
27172-
if (containsMissingType(type) && isAccessExpression(reference) && isMatchingReference(reference.expression, target) &&
27173-
getAccessedPropertyName(reference) === getPropertyNameFromType(leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType)) {
27174-
return getTypeWithFacts(type, assumeTrue ? TypeFacts.NEUndefined : TypeFacts.EQUndefined);
27175-
}
27176-
if (isMatchingReference(reference, target)) {
27177-
return narrowTypeByInKeyword(type, leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue);
27178-
}
27170+
let leftType: Type;
27171+
if (containsMissingType(type) && isAccessExpression(reference) && isMatchingReference(reference.expression, target) && (leftType = getTypeOfExpression(expr.left)).flags & TypeFlags.StringOrNumberLiteralOrUnique &&
27172+
getAccessedPropertyName(reference) === getPropertyNameFromType(leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType)) {
27173+
return getTypeWithFacts(type, assumeTrue ? TypeFacts.NEUndefined : TypeFacts.EQUndefined);
27174+
}
27175+
if (isMatchingReference(reference, target) && (leftType = getTypeOfExpression(expr.left)).flags & TypeFlags.StringOrNumberLiteralOrUnique) {
27176+
return narrowTypeByInKeyword(type, leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue);
2717927177
}
2718027178
break;
2718127179
case SyntaxKind.CommaToken:

tests/baselines/reference/controlFlowInOperator.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,45 @@ if (a in c) {
2727
if (d in c) {
2828
c; // never
2929
}
30+
31+
// repro from https://github.com/microsoft/TypeScript/issues/54790
32+
33+
function uniqueID_54790(
34+
id: string | undefined,
35+
seenIDs: { [key: string]: string }
36+
): string {
37+
if (id === undefined) {
38+
id = "1";
39+
}
40+
if (!(id in seenIDs)) {
41+
return id;
42+
}
43+
for (let i = 1; i < Number.MAX_VALUE; i++) {
44+
const newID = `${id}-${i}`;
45+
if (!(newID in seenIDs)) {
46+
return newID;
47+
}
48+
}
49+
throw Error("heat death of the universe");
50+
}
51+
52+
function uniqueID_54790_2(id: string | number, seenIDs: object) {
53+
id = "a";
54+
for (let i = 1; i < 3; i++) {
55+
const newID = `${id}`;
56+
if (newID in seenIDs) {
57+
}
58+
}
59+
}
60+
61+
function uniqueID_54790_3(id: string | number, seenIDs: object) {
62+
id = "a";
63+
for (let i = 1; i < 3; i++) {
64+
const newID = id;
65+
if (newID in seenIDs) {
66+
}
67+
}
68+
}
3069

3170

3271
//// [controlFlowInOperator.js]
@@ -47,3 +86,35 @@ if (a in c) {
4786
if (d in c) {
4887
c; // never
4988
}
89+
// repro from https://github.com/microsoft/TypeScript/issues/54790
90+
function uniqueID_54790(id, seenIDs) {
91+
if (id === undefined) {
92+
id = "1";
93+
}
94+
if (!(id in seenIDs)) {
95+
return id;
96+
}
97+
for (var i = 1; i < Number.MAX_VALUE; i++) {
98+
var newID = "".concat(id, "-").concat(i);
99+
if (!(newID in seenIDs)) {
100+
return newID;
101+
}
102+
}
103+
throw Error("heat death of the universe");
104+
}
105+
function uniqueID_54790_2(id, seenIDs) {
106+
id = "a";
107+
for (var i = 1; i < 3; i++) {
108+
var newID = "".concat(id);
109+
if (newID in seenIDs) {
110+
}
111+
}
112+
}
113+
function uniqueID_54790_3(id, seenIDs) {
114+
id = "a";
115+
for (var i = 1; i < 3; i++) {
116+
var newID = id;
117+
if (newID in seenIDs) {
118+
}
119+
}
120+
}

tests/baselines/reference/controlFlowInOperator.symbols

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,103 @@ if (d in c) {
6363
>c : Symbol(c, Decl(controlFlowInOperator.ts, 7, 13))
6464
}
6565

66+
// repro from https://github.com/microsoft/TypeScript/issues/54790
67+
68+
function uniqueID_54790(
69+
>uniqueID_54790 : Symbol(uniqueID_54790, Decl(controlFlowInOperator.ts, 25, 1))
70+
71+
id: string | undefined,
72+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
73+
74+
seenIDs: { [key: string]: string }
75+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 30, 25))
76+
>key : Symbol(key, Decl(controlFlowInOperator.ts, 31, 14))
77+
78+
): string {
79+
if (id === undefined) {
80+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
81+
>undefined : Symbol(undefined)
82+
83+
id = "1";
84+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
85+
}
86+
if (!(id in seenIDs)) {
87+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
88+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 30, 25))
89+
90+
return id;
91+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
92+
}
93+
for (let i = 1; i < Number.MAX_VALUE; i++) {
94+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 39, 10))
95+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 39, 10))
96+
>Number.MAX_VALUE : Symbol(NumberConstructor.MAX_VALUE, Decl(lib.es5.d.ts, --, --))
97+
>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
98+
>MAX_VALUE : Symbol(NumberConstructor.MAX_VALUE, Decl(lib.es5.d.ts, --, --))
99+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 39, 10))
100+
101+
const newID = `${id}-${i}`;
102+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 40, 9))
103+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 29, 24))
104+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 39, 10))
105+
106+
if (!(newID in seenIDs)) {
107+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 40, 9))
108+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 30, 25))
109+
110+
return newID;
111+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 40, 9))
112+
}
113+
}
114+
throw Error("heat death of the universe");
115+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
116+
}
117+
118+
function uniqueID_54790_2(id: string | number, seenIDs: object) {
119+
>uniqueID_54790_2 : Symbol(uniqueID_54790_2, Decl(controlFlowInOperator.ts, 46, 1))
120+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 48, 26))
121+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 48, 46))
122+
123+
id = "a";
124+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 48, 26))
125+
126+
for (let i = 1; i < 3; i++) {
127+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 50, 10))
128+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 50, 10))
129+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 50, 10))
130+
131+
const newID = `${id}`;
132+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 51, 9))
133+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 48, 26))
134+
135+
if (newID in seenIDs) {
136+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 51, 9))
137+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 48, 46))
138+
}
139+
}
140+
}
141+
142+
function uniqueID_54790_3(id: string | number, seenIDs: object) {
143+
>uniqueID_54790_3 : Symbol(uniqueID_54790_3, Decl(controlFlowInOperator.ts, 55, 1))
144+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 57, 26))
145+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 57, 46))
146+
147+
id = "a";
148+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 57, 26))
149+
150+
for (let i = 1; i < 3; i++) {
151+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 59, 10))
152+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 59, 10))
153+
>i : Symbol(i, Decl(controlFlowInOperator.ts, 59, 10))
154+
155+
const newID = id;
156+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 60, 9))
157+
>id : Symbol(id, Decl(controlFlowInOperator.ts, 57, 26))
158+
159+
if (newID in seenIDs) {
160+
>newID : Symbol(newID, Decl(controlFlowInOperator.ts, 60, 9))
161+
>seenIDs : Symbol(seenIDs, Decl(controlFlowInOperator.ts, 57, 46))
162+
}
163+
}
164+
}
165+

tests/baselines/reference/controlFlowInOperator.types

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,133 @@ if (d in c) {
7272
>c : (A | B) & Record<"d", unknown>
7373
}
7474

75+
// repro from https://github.com/microsoft/TypeScript/issues/54790
76+
77+
function uniqueID_54790(
78+
>uniqueID_54790 : (id: string | undefined, seenIDs: { [key: string]: string; }) => string
79+
80+
id: string | undefined,
81+
>id : string
82+
83+
seenIDs: { [key: string]: string }
84+
>seenIDs : { [key: string]: string; }
85+
>key : string
86+
87+
): string {
88+
if (id === undefined) {
89+
>id === undefined : boolean
90+
>id : string
91+
>undefined : undefined
92+
93+
id = "1";
94+
>id = "1" : "1"
95+
>id : string
96+
>"1" : "1"
97+
}
98+
if (!(id in seenIDs)) {
99+
>!(id in seenIDs) : boolean
100+
>(id in seenIDs) : boolean
101+
>id in seenIDs : boolean
102+
>id : string
103+
>seenIDs : { [key: string]: string; }
104+
105+
return id;
106+
>id : string
107+
}
108+
for (let i = 1; i < Number.MAX_VALUE; i++) {
109+
>i : number
110+
>1 : 1
111+
>i < Number.MAX_VALUE : boolean
112+
>i : number
113+
>Number.MAX_VALUE : number
114+
>Number : NumberConstructor
115+
>MAX_VALUE : number
116+
>i++ : number
117+
>i : number
118+
119+
const newID = `${id}-${i}`;
120+
>newID : string
121+
>`${id}-${i}` : string
122+
>id : string
123+
>i : number
124+
125+
if (!(newID in seenIDs)) {
126+
>!(newID in seenIDs) : boolean
127+
>(newID in seenIDs) : boolean
128+
>newID in seenIDs : boolean
129+
>newID : string
130+
>seenIDs : { [key: string]: string; }
131+
132+
return newID;
133+
>newID : string
134+
}
135+
}
136+
throw Error("heat death of the universe");
137+
>Error("heat death of the universe") : Error
138+
>Error : ErrorConstructor
139+
>"heat death of the universe" : "heat death of the universe"
140+
}
141+
142+
function uniqueID_54790_2(id: string | number, seenIDs: object) {
143+
>uniqueID_54790_2 : (id: string | number, seenIDs: object) => void
144+
>id : string | number
145+
>seenIDs : object
146+
147+
id = "a";
148+
>id = "a" : "a"
149+
>id : string | number
150+
>"a" : "a"
151+
152+
for (let i = 1; i < 3; i++) {
153+
>i : number
154+
>1 : 1
155+
>i < 3 : boolean
156+
>i : number
157+
>3 : 3
158+
>i++ : number
159+
>i : number
160+
161+
const newID = `${id}`;
162+
>newID : string
163+
>`${id}` : string
164+
>id : string
165+
166+
if (newID in seenIDs) {
167+
>newID in seenIDs : boolean
168+
>newID : string
169+
>seenIDs : object
170+
}
171+
}
172+
}
173+
174+
function uniqueID_54790_3(id: string | number, seenIDs: object) {
175+
>uniqueID_54790_3 : (id: string | number, seenIDs: object) => void
176+
>id : string | number
177+
>seenIDs : object
178+
179+
id = "a";
180+
>id = "a" : "a"
181+
>id : string | number
182+
>"a" : "a"
183+
184+
for (let i = 1; i < 3; i++) {
185+
>i : number
186+
>1 : 1
187+
>i < 3 : boolean
188+
>i : number
189+
>3 : 3
190+
>i++ : number
191+
>i : number
192+
193+
const newID = id;
194+
>newID : string
195+
>id : string
196+
197+
if (newID in seenIDs) {
198+
>newID in seenIDs : boolean
199+
>newID : string
200+
>seenIDs : object
201+
}
202+
}
203+
}
204+

tests/cases/conformance/controlFlow/controlFlowInOperator.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,42 @@ if (a in c) {
2424
if (d in c) {
2525
c; // never
2626
}
27+
28+
// repro from https://github.com/microsoft/TypeScript/issues/54790
29+
30+
function uniqueID_54790(
31+
id: string | undefined,
32+
seenIDs: { [key: string]: string }
33+
): string {
34+
if (id === undefined) {
35+
id = "1";
36+
}
37+
if (!(id in seenIDs)) {
38+
return id;
39+
}
40+
for (let i = 1; i < Number.MAX_VALUE; i++) {
41+
const newID = `${id}-${i}`;
42+
if (!(newID in seenIDs)) {
43+
return newID;
44+
}
45+
}
46+
throw Error("heat death of the universe");
47+
}
48+
49+
function uniqueID_54790_2(id: string | number, seenIDs: object) {
50+
id = "a";
51+
for (let i = 1; i < 3; i++) {
52+
const newID = `${id}`;
53+
if (newID in seenIDs) {
54+
}
55+
}
56+
}
57+
58+
function uniqueID_54790_3(id: string | number, seenIDs: object) {
59+
id = "a";
60+
for (let i = 1; i < 3; i++) {
61+
const newID = id;
62+
if (newID in seenIDs) {
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)