Skip to content

Commit 7e4a96e

Browse files
authored
Revise and simplify CFA for typeof check expressions (microsoft#49422)
* Revise and simplify CFA for `typeof` check expressions * Accept new baselines * Add regression test * Slight change to preserve type when related in both directions * Add regression test * Explain reasons for exact sequence of type checks
1 parent 5cedf3e commit 7e4a96e

File tree

8 files changed

+181
-187
lines changed

8 files changed

+181
-187
lines changed

src/compiler/checker.ts

Lines changed: 68 additions & 178 deletions
Large diffs are not rendered by default.

tests/baselines/reference/mappedTypes4.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function boxify<T>(obj: T): Boxified<T> {
2727

2828
for (let k in obj) {
2929
>k : Extract<keyof T, string>
30-
>obj : (T & null) | (T & object)
30+
>obj : (T & object) | (T & null)
3131

3232
result[k] = { value: obj[k] };
3333
>result[k] = { value: obj[k] } : { value: (T & object)[Extract<keyof T, string>]; }

tests/baselines/reference/typeGuardOfFormTypeOfFunction.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
7171
item.call(obj);
7272
}
7373
}
74+
75+
// Repro from #49316
76+
77+
function configureStore<S extends object>(reducer: (() => void) | Record<keyof S, () => void>) {
78+
let rootReducer: () => void;
79+
if (typeof reducer === 'function') {
80+
rootReducer = reducer;
81+
}
82+
}
83+
84+
function f101(x: string | Record<string, any>) {
85+
return typeof x === "object" && x.anything;
86+
}
7487

7588

7689
//// [typeGuardOfFormTypeOfFunction.js]
@@ -137,3 +150,13 @@ function f100(obj, keys) {
137150
item.call(obj);
138151
}
139152
}
153+
// Repro from #49316
154+
function configureStore(reducer) {
155+
var rootReducer;
156+
if (typeof reducer === 'function') {
157+
rootReducer = reducer;
158+
}
159+
}
160+
function f101(x) {
161+
return typeof x === "object" && x.anything;
162+
}

tests/baselines/reference/typeGuardOfFormTypeOfFunction.symbols

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,34 @@ function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
157157
}
158158
}
159159

160+
// Repro from #49316
161+
162+
function configureStore<S extends object>(reducer: (() => void) | Record<keyof S, () => void>) {
163+
>configureStore : Symbol(configureStore, Decl(typeGuardOfFormTypeOfFunction.ts, 71, 1))
164+
>S : Symbol(S, Decl(typeGuardOfFormTypeOfFunction.ts, 75, 24))
165+
>reducer : Symbol(reducer, Decl(typeGuardOfFormTypeOfFunction.ts, 75, 42))
166+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
167+
>S : Symbol(S, Decl(typeGuardOfFormTypeOfFunction.ts, 75, 24))
168+
169+
let rootReducer: () => void;
170+
>rootReducer : Symbol(rootReducer, Decl(typeGuardOfFormTypeOfFunction.ts, 76, 7))
171+
172+
if (typeof reducer === 'function') {
173+
>reducer : Symbol(reducer, Decl(typeGuardOfFormTypeOfFunction.ts, 75, 42))
174+
175+
rootReducer = reducer;
176+
>rootReducer : Symbol(rootReducer, Decl(typeGuardOfFormTypeOfFunction.ts, 76, 7))
177+
>reducer : Symbol(reducer, Decl(typeGuardOfFormTypeOfFunction.ts, 75, 42))
178+
}
179+
}
180+
181+
function f101(x: string | Record<string, any>) {
182+
>f101 : Symbol(f101, Decl(typeGuardOfFormTypeOfFunction.ts, 80, 1))
183+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 82, 14))
184+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
185+
186+
return typeof x === "object" && x.anything;
187+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 82, 14))
188+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 82, 14))
189+
}
190+

tests/baselines/reference/typeGuardOfFormTypeOfFunction.types

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,40 @@ function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
182182
}
183183
}
184184

185+
// Repro from #49316
186+
187+
function configureStore<S extends object>(reducer: (() => void) | Record<keyof S, () => void>) {
188+
>configureStore : <S extends object>(reducer: (() => void) | Record<keyof S, () => void>) => void
189+
>reducer : Record<keyof S, () => void> | (() => void)
190+
191+
let rootReducer: () => void;
192+
>rootReducer : () => void
193+
194+
if (typeof reducer === 'function') {
195+
>typeof reducer === 'function' : boolean
196+
>typeof reducer : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
197+
>reducer : Record<keyof S, () => void> | (() => void)
198+
>'function' : "function"
199+
200+
rootReducer = reducer;
201+
>rootReducer = reducer : () => void
202+
>rootReducer : () => void
203+
>reducer : () => void
204+
}
205+
}
206+
207+
function f101(x: string | Record<string, any>) {
208+
>f101 : (x: string | Record<string, any>) => any
209+
>x : string | Record<string, any>
210+
211+
return typeof x === "object" && x.anything;
212+
>typeof x === "object" && x.anything : any
213+
>typeof x === "object" : boolean
214+
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
215+
>x : string | Record<string, any>
216+
>"object" : "object"
217+
>x.anything : any
218+
>x : Record<string, any>
219+
>anything : any
220+
}
221+

tests/baselines/reference/typeGuardTypeOfUndefined.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ function test9(a: boolean | number) {
242242
}
243243
else {
244244
a;
245-
>a : never
245+
>a : undefined
246246
}
247247
}
248248

@@ -259,15 +259,15 @@ function test10(a: boolean | number) {
259259
if (typeof a === "boolean") {
260260
>typeof a === "boolean" : boolean
261261
>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
262-
>a : never
262+
>a : undefined
263263
>"boolean" : "boolean"
264264

265265
a;
266266
>a : never
267267
}
268268
else {
269269
a;
270-
>a : never
270+
>a : undefined
271271
}
272272
}
273273
else {

tests/baselines/reference/unknownControlFlow.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ function f31<T>(x: T) {
410410
>"object" : "object"
411411

412412
x; // T & object | T & null
413-
>x : (T & null) | (T & object)
413+
>x : (T & object) | (T & null)
414414
}
415415
if (x && typeof x === "object") {
416416
>x && typeof x === "object" : boolean
@@ -424,12 +424,12 @@ function f31<T>(x: T) {
424424
>x : T & object
425425
}
426426
if (typeof x === "object" && x) {
427-
>typeof x === "object" && x : false | (T & null) | (T & object)
427+
>typeof x === "object" && x : false | (T & object) | (T & null)
428428
>typeof x === "object" : boolean
429429
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
430430
>x : T
431431
>"object" : "object"
432-
>x : (T & null) | (T & object)
432+
>x : (T & object) | (T & null)
433433

434434
x; // T & object
435435
>x : T & object
@@ -650,9 +650,9 @@ function deepEquals<T>(a: T, b: T): boolean {
650650
>b : T
651651
>'object' : "object"
652652
>!a : boolean
653-
>a : (T & null) | (T & object)
653+
>a : (T & object) | (T & null)
654654
>!b : boolean
655-
>b : (T & null) | (T & object)
655+
>b : (T & object) | (T & null)
656656

657657
return false;
658658
>false : false

tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfFunction.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,16 @@ function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
7171
item.call(obj);
7272
}
7373
}
74+
75+
// Repro from #49316
76+
77+
function configureStore<S extends object>(reducer: (() => void) | Record<keyof S, () => void>) {
78+
let rootReducer: () => void;
79+
if (typeof reducer === 'function') {
80+
rootReducer = reducer;
81+
}
82+
}
83+
84+
function f101(x: string | Record<string, any>) {
85+
return typeof x === "object" && x.anything;
86+
}

0 commit comments

Comments
 (0)