Skip to content

Commit 667de4b

Browse files
authored
Merge pull request microsoft#24627 from Microsoft/typeofFunction
Fix typeof x === "function" type guards
2 parents 64c3665 + 8b38d35 commit 667de4b

File tree

5 files changed

+566
-1
lines changed

5 files changed

+566
-1
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14112,11 +14112,14 @@ namespace ts {
1411214112
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
1411314113
assumeTrue = !assumeTrue;
1411414114
}
14115+
if (type.flags & TypeFlags.Any && literal.text === "function") {
14116+
return type;
14117+
}
1411514118
if (assumeTrue && !(type.flags & TypeFlags.Union)) {
1411614119
// We narrow a non-union type to an exact primitive type if the non-union type
1411714120
// is a supertype of that primitive type. For example, type 'any' can be narrowed
1411814121
// to one of the primitive types.
14119-
const targetType = typeofTypesByName.get(literal.text);
14122+
const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
1412014123
if (targetType) {
1412114124
if (isTypeSubtypeOf(targetType, type)) {
1412214125
return targetType;
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//// [typeGuardOfFormTypeOfFunction.ts]
2+
function f1(x: any) {
3+
if (typeof x === "function") {
4+
x; // any
5+
}
6+
}
7+
8+
function f2(x: unknown) {
9+
if (typeof x === "function") {
10+
x; // Function
11+
}
12+
}
13+
14+
function f3(x: {}) {
15+
if (typeof x === "function") {
16+
x; // Function
17+
}
18+
}
19+
20+
function f4<T>(x: T) {
21+
if (typeof x === "function") {
22+
x; // T & Function
23+
}
24+
}
25+
26+
function f5(x: { s: string }) {
27+
if (typeof x === "function") {
28+
x; // never
29+
}
30+
}
31+
32+
function f6(x: () => string) {
33+
if (typeof x === "function") {
34+
x; // () => string
35+
}
36+
}
37+
38+
function f10(x: string | (() => string)) {
39+
if (typeof x === "function") {
40+
x; // () => string
41+
}
42+
else {
43+
x; // string
44+
}
45+
}
46+
47+
function f11(x: { s: string } | (() => string)) {
48+
if (typeof x === "function") {
49+
x; // () => string
50+
}
51+
else {
52+
x; // { s: string }
53+
}
54+
}
55+
56+
function f12(x: { s: string } | { n: number }) {
57+
if (typeof x === "function") {
58+
x; // never
59+
}
60+
else {
61+
x; // { s: string } | { n: number }
62+
}
63+
}
64+
65+
// Repro from #18238
66+
67+
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
68+
for (const k of keys) {
69+
const item = obj[k];
70+
if (typeof item == 'function')
71+
item.call(obj);
72+
}
73+
}
74+
75+
76+
//// [typeGuardOfFormTypeOfFunction.js]
77+
function f1(x) {
78+
if (typeof x === "function") {
79+
x; // any
80+
}
81+
}
82+
function f2(x) {
83+
if (typeof x === "function") {
84+
x; // Function
85+
}
86+
}
87+
function f3(x) {
88+
if (typeof x === "function") {
89+
x; // Function
90+
}
91+
}
92+
function f4(x) {
93+
if (typeof x === "function") {
94+
x; // T & Function
95+
}
96+
}
97+
function f5(x) {
98+
if (typeof x === "function") {
99+
x; // never
100+
}
101+
}
102+
function f6(x) {
103+
if (typeof x === "function") {
104+
x; // () => string
105+
}
106+
}
107+
function f10(x) {
108+
if (typeof x === "function") {
109+
x; // () => string
110+
}
111+
else {
112+
x; // string
113+
}
114+
}
115+
function f11(x) {
116+
if (typeof x === "function") {
117+
x; // () => string
118+
}
119+
else {
120+
x; // { s: string }
121+
}
122+
}
123+
function f12(x) {
124+
if (typeof x === "function") {
125+
x; // never
126+
}
127+
else {
128+
x; // { s: string } | { n: number }
129+
}
130+
}
131+
// Repro from #18238
132+
function f100(obj, keys) {
133+
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
134+
var k = keys_1[_i];
135+
var item = obj[k];
136+
if (typeof item == 'function')
137+
item.call(obj);
138+
}
139+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormTypeOfFunction.ts ===
2+
function f1(x: any) {
3+
>f1 : Symbol(f1, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 0))
4+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
5+
6+
if (typeof x === "function") {
7+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
8+
9+
x; // any
10+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 0, 12))
11+
}
12+
}
13+
14+
function f2(x: unknown) {
15+
>f2 : Symbol(f2, Decl(typeGuardOfFormTypeOfFunction.ts, 4, 1))
16+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
17+
18+
if (typeof x === "function") {
19+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
20+
21+
x; // Function
22+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 6, 12))
23+
}
24+
}
25+
26+
function f3(x: {}) {
27+
>f3 : Symbol(f3, Decl(typeGuardOfFormTypeOfFunction.ts, 10, 1))
28+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
29+
30+
if (typeof x === "function") {
31+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
32+
33+
x; // Function
34+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 12, 12))
35+
}
36+
}
37+
38+
function f4<T>(x: T) {
39+
>f4 : Symbol(f4, Decl(typeGuardOfFormTypeOfFunction.ts, 16, 1))
40+
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 12))
41+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
42+
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 12))
43+
44+
if (typeof x === "function") {
45+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
46+
47+
x; // T & Function
48+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 18, 15))
49+
}
50+
}
51+
52+
function f5(x: { s: string }) {
53+
>f5 : Symbol(f5, Decl(typeGuardOfFormTypeOfFunction.ts, 22, 1))
54+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
55+
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 16))
56+
57+
if (typeof x === "function") {
58+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
59+
60+
x; // never
61+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 24, 12))
62+
}
63+
}
64+
65+
function f6(x: () => string) {
66+
>f6 : Symbol(f6, Decl(typeGuardOfFormTypeOfFunction.ts, 28, 1))
67+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
68+
69+
if (typeof x === "function") {
70+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
71+
72+
x; // () => string
73+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 30, 12))
74+
}
75+
}
76+
77+
function f10(x: string | (() => string)) {
78+
>f10 : Symbol(f10, Decl(typeGuardOfFormTypeOfFunction.ts, 34, 1))
79+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
80+
81+
if (typeof x === "function") {
82+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
83+
84+
x; // () => string
85+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
86+
}
87+
else {
88+
x; // string
89+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 36, 13))
90+
}
91+
}
92+
93+
function f11(x: { s: string } | (() => string)) {
94+
>f11 : Symbol(f11, Decl(typeGuardOfFormTypeOfFunction.ts, 43, 1))
95+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
96+
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 17))
97+
98+
if (typeof x === "function") {
99+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
100+
101+
x; // () => string
102+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
103+
}
104+
else {
105+
x; // { s: string }
106+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 45, 13))
107+
}
108+
}
109+
110+
function f12(x: { s: string } | { n: number }) {
111+
>f12 : Symbol(f12, Decl(typeGuardOfFormTypeOfFunction.ts, 52, 1))
112+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
113+
>s : Symbol(s, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 17))
114+
>n : Symbol(n, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 33))
115+
116+
if (typeof x === "function") {
117+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
118+
119+
x; // never
120+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
121+
}
122+
else {
123+
x; // { s: string } | { n: number }
124+
>x : Symbol(x, Decl(typeGuardOfFormTypeOfFunction.ts, 54, 13))
125+
}
126+
}
127+
128+
// Repro from #18238
129+
130+
function f100<T, K extends keyof T>(obj: T, keys: K[]) : void {
131+
>f100 : Symbol(f100, Decl(typeGuardOfFormTypeOfFunction.ts, 61, 1))
132+
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
133+
>K : Symbol(K, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 16))
134+
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
135+
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
136+
>T : Symbol(T, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 14))
137+
>keys : Symbol(keys, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 43))
138+
>K : Symbol(K, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 16))
139+
140+
for (const k of keys) {
141+
>k : Symbol(k, Decl(typeGuardOfFormTypeOfFunction.ts, 66, 14))
142+
>keys : Symbol(keys, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 43))
143+
144+
const item = obj[k];
145+
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
146+
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
147+
>k : Symbol(k, Decl(typeGuardOfFormTypeOfFunction.ts, 66, 14))
148+
149+
if (typeof item == 'function')
150+
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
151+
152+
item.call(obj);
153+
>item.call : Symbol(Function.call, Decl(lib.d.ts, --, --))
154+
>item : Symbol(item, Decl(typeGuardOfFormTypeOfFunction.ts, 67, 13))
155+
>call : Symbol(Function.call, Decl(lib.d.ts, --, --))
156+
>obj : Symbol(obj, Decl(typeGuardOfFormTypeOfFunction.ts, 65, 36))
157+
}
158+
}
159+

0 commit comments

Comments
 (0)