Skip to content

Commit 451b5ba

Browse files
TypeScript Botandrewbranch
TypeScript Bot
andauthored
Cherry-pick PR microsoft#50601 into release-4.8 (microsoft#50613)
Component commits: 8fa47f1 Allow `{}` to narrow in same special cases as `unknown` Co-authored-by: Andrew Branch <andrew@wheream.io>
1 parent 4083b46 commit 451b5ba

8 files changed

+942
-2
lines changed

src/compiler/checker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -25255,14 +25255,19 @@ namespace ts {
2525525255
assumeTrue = !assumeTrue;
2525625256
}
2525725257
const valueType = getTypeOfExpression(value);
25258-
if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
25258+
if (((type.flags & TypeFlags.Unknown) || isEmptyAnonymousObjectType(type) && !(valueType.flags & TypeFlags.Nullable)) &&
25259+
assumeTrue &&
25260+
(operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)
25261+
) {
2525925262
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
2526025263
return valueType;
2526125264
}
2526225265
if (valueType.flags & TypeFlags.Object) {
2526325266
return nonPrimitiveType;
2526425267
}
25265-
return type;
25268+
if (type.flags & TypeFlags.Unknown) {
25269+
return type;
25270+
}
2526625271
}
2526725272
if (valueType.flags & TypeFlags.Nullable) {
2526825273
if (!strictNullChecks) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//// [emptyAnonymousObjectNarrowing.ts]
2+
declare let nonNull: {};
3+
if (nonNull === "foo") {
4+
nonNull;
5+
}
6+
else {
7+
nonNull;
8+
}
9+
10+
declare let obj: { a: string };
11+
if (nonNull === obj) {
12+
nonNull;
13+
}
14+
else {
15+
nonNull;
16+
}
17+
18+
function f1<T>(x: T) {
19+
if (nonNull === x) {
20+
nonNull;
21+
}
22+
else {
23+
nonNull;
24+
}
25+
}
26+
27+
function f2<T extends object>(x: T) {
28+
if (nonNull === x) {
29+
nonNull;
30+
}
31+
else {
32+
nonNull;
33+
}
34+
}
35+
36+
declare let union: "xyz" | { a: string } | undefined;
37+
if (nonNull === union) {
38+
nonNull;
39+
}
40+
else {
41+
nonNull;
42+
}
43+
44+
if (nonNull === undefined) {
45+
nonNull;
46+
}
47+
else {
48+
nonNull;
49+
}
50+
51+
if (nonNull === null) {
52+
nonNull;
53+
}
54+
else {
55+
nonNull;
56+
}
57+
58+
if (nonNull == undefined) {
59+
nonNull;
60+
}
61+
else {
62+
nonNull;
63+
}
64+
65+
// Repro from #50567
66+
const foo = (value: unknown): string => {
67+
if (!value) {
68+
return 'foo';
69+
}
70+
if (value === 'xyz') {
71+
return value; // Type '{}' is not assignable to type 'string'.
72+
}
73+
return '';
74+
};
75+
76+
77+
//// [emptyAnonymousObjectNarrowing.js]
78+
if (nonNull === "foo") {
79+
nonNull;
80+
}
81+
else {
82+
nonNull;
83+
}
84+
if (nonNull === obj) {
85+
nonNull;
86+
}
87+
else {
88+
nonNull;
89+
}
90+
function f1(x) {
91+
if (nonNull === x) {
92+
nonNull;
93+
}
94+
else {
95+
nonNull;
96+
}
97+
}
98+
function f2(x) {
99+
if (nonNull === x) {
100+
nonNull;
101+
}
102+
else {
103+
nonNull;
104+
}
105+
}
106+
if (nonNull === union) {
107+
nonNull;
108+
}
109+
else {
110+
nonNull;
111+
}
112+
if (nonNull === undefined) {
113+
nonNull;
114+
}
115+
else {
116+
nonNull;
117+
}
118+
if (nonNull === null) {
119+
nonNull;
120+
}
121+
else {
122+
nonNull;
123+
}
124+
if (nonNull == undefined) {
125+
nonNull;
126+
}
127+
else {
128+
nonNull;
129+
}
130+
// Repro from #50567
131+
var foo = function (value) {
132+
if (!value) {
133+
return 'foo';
134+
}
135+
if (value === 'xyz') {
136+
return value; // Type '{}' is not assignable to type 'string'.
137+
}
138+
return '';
139+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
=== tests/cases/compiler/emptyAnonymousObjectNarrowing.ts ===
2+
declare let nonNull: {};
3+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
4+
5+
if (nonNull === "foo") {
6+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
7+
8+
nonNull;
9+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
10+
}
11+
else {
12+
nonNull;
13+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
14+
}
15+
16+
declare let obj: { a: string };
17+
>obj : Symbol(obj, Decl(emptyAnonymousObjectNarrowing.ts, 8, 11))
18+
>a : Symbol(a, Decl(emptyAnonymousObjectNarrowing.ts, 8, 18))
19+
20+
if (nonNull === obj) {
21+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
22+
>obj : Symbol(obj, Decl(emptyAnonymousObjectNarrowing.ts, 8, 11))
23+
24+
nonNull;
25+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
26+
}
27+
else {
28+
nonNull;
29+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
30+
}
31+
32+
function f1<T>(x: T) {
33+
>f1 : Symbol(f1, Decl(emptyAnonymousObjectNarrowing.ts, 14, 1))
34+
>T : Symbol(T, Decl(emptyAnonymousObjectNarrowing.ts, 16, 12))
35+
>x : Symbol(x, Decl(emptyAnonymousObjectNarrowing.ts, 16, 15))
36+
>T : Symbol(T, Decl(emptyAnonymousObjectNarrowing.ts, 16, 12))
37+
38+
if (nonNull === x) {
39+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
40+
>x : Symbol(x, Decl(emptyAnonymousObjectNarrowing.ts, 16, 15))
41+
42+
nonNull;
43+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
44+
}
45+
else {
46+
nonNull;
47+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
48+
}
49+
}
50+
51+
function f2<T extends object>(x: T) {
52+
>f2 : Symbol(f2, Decl(emptyAnonymousObjectNarrowing.ts, 23, 1))
53+
>T : Symbol(T, Decl(emptyAnonymousObjectNarrowing.ts, 25, 12))
54+
>x : Symbol(x, Decl(emptyAnonymousObjectNarrowing.ts, 25, 30))
55+
>T : Symbol(T, Decl(emptyAnonymousObjectNarrowing.ts, 25, 12))
56+
57+
if (nonNull === x) {
58+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
59+
>x : Symbol(x, Decl(emptyAnonymousObjectNarrowing.ts, 25, 30))
60+
61+
nonNull;
62+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
63+
}
64+
else {
65+
nonNull;
66+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
67+
}
68+
}
69+
70+
declare let union: "xyz" | { a: string } | undefined;
71+
>union : Symbol(union, Decl(emptyAnonymousObjectNarrowing.ts, 34, 11))
72+
>a : Symbol(a, Decl(emptyAnonymousObjectNarrowing.ts, 34, 28))
73+
74+
if (nonNull === union) {
75+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
76+
>union : Symbol(union, Decl(emptyAnonymousObjectNarrowing.ts, 34, 11))
77+
78+
nonNull;
79+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
80+
}
81+
else {
82+
nonNull;
83+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
84+
}
85+
86+
if (nonNull === undefined) {
87+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
88+
>undefined : Symbol(undefined)
89+
90+
nonNull;
91+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
92+
}
93+
else {
94+
nonNull;
95+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
96+
}
97+
98+
if (nonNull === null) {
99+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
100+
101+
nonNull;
102+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
103+
}
104+
else {
105+
nonNull;
106+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
107+
}
108+
109+
if (nonNull == undefined) {
110+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
111+
>undefined : Symbol(undefined)
112+
113+
nonNull;
114+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
115+
}
116+
else {
117+
nonNull;
118+
>nonNull : Symbol(nonNull, Decl(emptyAnonymousObjectNarrowing.ts, 0, 11))
119+
}
120+
121+
// Repro from #50567
122+
const foo = (value: unknown): string => {
123+
>foo : Symbol(foo, Decl(emptyAnonymousObjectNarrowing.ts, 64, 5))
124+
>value : Symbol(value, Decl(emptyAnonymousObjectNarrowing.ts, 64, 13))
125+
126+
if (!value) {
127+
>value : Symbol(value, Decl(emptyAnonymousObjectNarrowing.ts, 64, 13))
128+
129+
return 'foo';
130+
}
131+
if (value === 'xyz') {
132+
>value : Symbol(value, Decl(emptyAnonymousObjectNarrowing.ts, 64, 13))
133+
134+
return value; // Type '{}' is not assignable to type 'string'.
135+
>value : Symbol(value, Decl(emptyAnonymousObjectNarrowing.ts, 64, 13))
136+
}
137+
return '';
138+
};
139+

0 commit comments

Comments
 (0)