Skip to content

Commit 0dda037

Browse files
authored
Improve unknown narrowing by negated type predicates (#60795)
1 parent 52717ac commit 0dda037

File tree

4 files changed

+264
-1
lines changed

4 files changed

+264
-1
lines changed

Diff for: src/compiler/checker.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -29404,8 +29404,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2940429404
if (checkDerived) {
2940529405
return filterType(type, t => !isTypeDerivedFrom(t, candidate));
2940629406
}
29407+
type = type.flags & TypeFlags.Unknown ? unknownUnionType : type;
2940729408
const trueType = getNarrowedType(type, candidate, /*assumeTrue*/ true, /*checkDerived*/ false);
29408-
return filterType(type, t => !isTypeSubsetOf(t, trueType));
29409+
return recombineUnknownType(filterType(type, t => !isTypeSubsetOf(t, trueType)));
2940929410
}
2941029411
if (type.flags & TypeFlags.AnyOrUnknown) {
2941129412
return candidate;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//// [tests/cases/compiler/narrowUnknownByTypePredicate.ts] ////
2+
3+
=== narrowUnknownByTypePredicate.ts ===
4+
declare function isNotNullish(value: unknown): value is {};
5+
>isNotNullish : Symbol(isNotNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 0))
6+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 0, 30))
7+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 0, 30))
8+
9+
declare function isNullish(value: unknown): value is null | undefined;
10+
>isNullish : Symbol(isNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 59))
11+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 1, 27))
12+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 1, 27))
13+
14+
declare const value1: unknown;
15+
>value1 : Symbol(value1, Decl(narrowUnknownByTypePredicate.ts, 3, 13))
16+
17+
if (isNotNullish(value1)) {
18+
>isNotNullish : Symbol(isNotNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 0))
19+
>value1 : Symbol(value1, Decl(narrowUnknownByTypePredicate.ts, 3, 13))
20+
21+
value1;
22+
>value1 : Symbol(value1, Decl(narrowUnknownByTypePredicate.ts, 3, 13))
23+
}
24+
25+
declare const value2: unknown;
26+
>value2 : Symbol(value2, Decl(narrowUnknownByTypePredicate.ts, 8, 13))
27+
28+
if (!isNotNullish(value2)) {
29+
>isNotNullish : Symbol(isNotNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 0))
30+
>value2 : Symbol(value2, Decl(narrowUnknownByTypePredicate.ts, 8, 13))
31+
32+
value2;
33+
>value2 : Symbol(value2, Decl(narrowUnknownByTypePredicate.ts, 8, 13))
34+
}
35+
36+
declare const value3: unknown;
37+
>value3 : Symbol(value3, Decl(narrowUnknownByTypePredicate.ts, 13, 13))
38+
39+
if (isNullish(value3)) {
40+
>isNullish : Symbol(isNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 59))
41+
>value3 : Symbol(value3, Decl(narrowUnknownByTypePredicate.ts, 13, 13))
42+
43+
value3;
44+
>value3 : Symbol(value3, Decl(narrowUnknownByTypePredicate.ts, 13, 13))
45+
}
46+
47+
declare const value4: unknown;
48+
>value4 : Symbol(value4, Decl(narrowUnknownByTypePredicate.ts, 18, 13))
49+
50+
if (!isNullish(value4)) {
51+
>isNullish : Symbol(isNullish, Decl(narrowUnknownByTypePredicate.ts, 0, 59))
52+
>value4 : Symbol(value4, Decl(narrowUnknownByTypePredicate.ts, 18, 13))
53+
54+
value4;
55+
>value4 : Symbol(value4, Decl(narrowUnknownByTypePredicate.ts, 18, 13))
56+
}
57+
58+
declare class A { foo: string; }
59+
>A : Symbol(A, Decl(narrowUnknownByTypePredicate.ts, 21, 1))
60+
>foo : Symbol(A.foo, Decl(narrowUnknownByTypePredicate.ts, 23, 17))
61+
62+
declare function isA(value: unknown): value is A;
63+
>isA : Symbol(isA, Decl(narrowUnknownByTypePredicate.ts, 23, 32))
64+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 24, 21))
65+
>value : Symbol(value, Decl(narrowUnknownByTypePredicate.ts, 24, 21))
66+
>A : Symbol(A, Decl(narrowUnknownByTypePredicate.ts, 21, 1))
67+
68+
declare const value5: unknown;
69+
>value5 : Symbol(value5, Decl(narrowUnknownByTypePredicate.ts, 26, 13))
70+
71+
if (isA(value5)) {
72+
>isA : Symbol(isA, Decl(narrowUnknownByTypePredicate.ts, 23, 32))
73+
>value5 : Symbol(value5, Decl(narrowUnknownByTypePredicate.ts, 26, 13))
74+
75+
value5;
76+
>value5 : Symbol(value5, Decl(narrowUnknownByTypePredicate.ts, 26, 13))
77+
}
78+
79+
declare const value6: unknown;
80+
>value6 : Symbol(value6, Decl(narrowUnknownByTypePredicate.ts, 31, 13))
81+
82+
if (!isA(value6)) {
83+
>isA : Symbol(isA, Decl(narrowUnknownByTypePredicate.ts, 23, 32))
84+
>value6 : Symbol(value6, Decl(narrowUnknownByTypePredicate.ts, 31, 13))
85+
86+
value6;
87+
>value6 : Symbol(value6, Decl(narrowUnknownByTypePredicate.ts, 31, 13))
88+
}
89+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//// [tests/cases/compiler/narrowUnknownByTypePredicate.ts] ////
2+
3+
=== narrowUnknownByTypePredicate.ts ===
4+
declare function isNotNullish(value: unknown): value is {};
5+
>isNotNullish : (value: unknown) => value is {}
6+
> : ^ ^^ ^^^^^
7+
>value : unknown
8+
> : ^^^^^^^
9+
10+
declare function isNullish(value: unknown): value is null | undefined;
11+
>isNullish : (value: unknown) => value is null | undefined
12+
> : ^ ^^ ^^^^^
13+
>value : unknown
14+
> : ^^^^^^^
15+
16+
declare const value1: unknown;
17+
>value1 : unknown
18+
> : ^^^^^^^
19+
20+
if (isNotNullish(value1)) {
21+
>isNotNullish(value1) : boolean
22+
> : ^^^^^^^
23+
>isNotNullish : (value: unknown) => value is {}
24+
> : ^ ^^ ^^^^^
25+
>value1 : unknown
26+
> : ^^^^^^^
27+
28+
value1;
29+
>value1 : {}
30+
> : ^^
31+
}
32+
33+
declare const value2: unknown;
34+
>value2 : unknown
35+
> : ^^^^^^^
36+
37+
if (!isNotNullish(value2)) {
38+
>!isNotNullish(value2) : boolean
39+
> : ^^^^^^^
40+
>isNotNullish(value2) : boolean
41+
> : ^^^^^^^
42+
>isNotNullish : (value: unknown) => value is {}
43+
> : ^ ^^ ^^^^^
44+
>value2 : unknown
45+
> : ^^^^^^^
46+
47+
value2;
48+
>value2 : null | undefined
49+
> : ^^^^^^^^^^^^^^^^
50+
}
51+
52+
declare const value3: unknown;
53+
>value3 : unknown
54+
> : ^^^^^^^
55+
56+
if (isNullish(value3)) {
57+
>isNullish(value3) : boolean
58+
> : ^^^^^^^
59+
>isNullish : (value: unknown) => value is null | undefined
60+
> : ^ ^^ ^^^^^
61+
>value3 : unknown
62+
> : ^^^^^^^
63+
64+
value3;
65+
>value3 : null | undefined
66+
> : ^^^^^^^^^^^^^^^^
67+
}
68+
69+
declare const value4: unknown;
70+
>value4 : unknown
71+
> : ^^^^^^^
72+
73+
if (!isNullish(value4)) {
74+
>!isNullish(value4) : boolean
75+
> : ^^^^^^^
76+
>isNullish(value4) : boolean
77+
> : ^^^^^^^
78+
>isNullish : (value: unknown) => value is null | undefined
79+
> : ^ ^^ ^^^^^
80+
>value4 : unknown
81+
> : ^^^^^^^
82+
83+
value4;
84+
>value4 : {}
85+
> : ^^
86+
}
87+
88+
declare class A { foo: string; }
89+
>A : A
90+
> : ^
91+
>foo : string
92+
> : ^^^^^^
93+
94+
declare function isA(value: unknown): value is A;
95+
>isA : (value: unknown) => value is A
96+
> : ^ ^^ ^^^^^
97+
>value : unknown
98+
> : ^^^^^^^
99+
100+
declare const value5: unknown;
101+
>value5 : unknown
102+
> : ^^^^^^^
103+
104+
if (isA(value5)) {
105+
>isA(value5) : boolean
106+
> : ^^^^^^^
107+
>isA : (value: unknown) => value is A
108+
> : ^ ^^ ^^^^^
109+
>value5 : unknown
110+
> : ^^^^^^^
111+
112+
value5;
113+
>value5 : A
114+
> : ^
115+
}
116+
117+
declare const value6: unknown;
118+
>value6 : unknown
119+
> : ^^^^^^^
120+
121+
if (!isA(value6)) {
122+
>!isA(value6) : boolean
123+
> : ^^^^^^^
124+
>isA(value6) : boolean
125+
> : ^^^^^^^
126+
>isA : (value: unknown) => value is A
127+
> : ^ ^^ ^^^^^
128+
>value6 : unknown
129+
> : ^^^^^^^
130+
131+
value6;
132+
>value6 : unknown
133+
> : ^^^^^^^
134+
}
135+

Diff for: tests/cases/compiler/narrowUnknownByTypePredicate.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
declare function isNotNullish(value: unknown): value is {};
5+
declare function isNullish(value: unknown): value is null | undefined;
6+
7+
declare const value1: unknown;
8+
if (isNotNullish(value1)) {
9+
value1;
10+
}
11+
12+
declare const value2: unknown;
13+
if (!isNotNullish(value2)) {
14+
value2;
15+
}
16+
17+
declare const value3: unknown;
18+
if (isNullish(value3)) {
19+
value3;
20+
}
21+
22+
declare const value4: unknown;
23+
if (!isNullish(value4)) {
24+
value4;
25+
}
26+
27+
declare class A { foo: string; }
28+
declare function isA(value: unknown): value is A;
29+
30+
declare const value5: unknown;
31+
if (isA(value5)) {
32+
value5;
33+
}
34+
35+
declare const value6: unknown;
36+
if (!isA(value6)) {
37+
value6;
38+
}

0 commit comments

Comments
 (0)