Skip to content

Commit 2df6bd1

Browse files
committed
Add narrowing for unknown with strict equal
1 parent d8f736d commit 2df6bd1

File tree

5 files changed

+408
-0
lines changed

5 files changed

+408
-0
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14920,6 +14920,9 @@ namespace ts {
1492014920
assumeTrue = !assumeTrue;
1492114921
}
1492214922
const valueType = getTypeOfExpression(value);
14923+
if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && (valueType.flags & TypeFlags.Unit)) {
14924+
return assumeTrue ? valueType : type;
14925+
}
1492314926
if (valueType.flags & TypeFlags.Nullable) {
1492414927
if (!strictNullChecks) {
1492514928
return type;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//// [unknownType2.ts]
2+
type isUnknown<T> = unknown extends T ? true : false;
3+
type isTrue<T extends true> = T;
4+
5+
type SomeResponse = 'yes' | 'no' | 'idk';
6+
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
7+
8+
const u: unknown = undefined;
9+
10+
declare const symb: unique symbol;
11+
12+
if (u === 5) {
13+
const y = u.toString(10);
14+
}
15+
16+
if (u === true || u === false) {
17+
const someBool: boolean = u;
18+
}
19+
20+
if (u === undefined) {
21+
const undef: undefined = u;
22+
}
23+
24+
if (u === null) {
25+
const someNull: null = u;
26+
}
27+
28+
if(u === symb) {
29+
const symbolAlias: typeof symb = u;
30+
}
31+
32+
if (!(u === 42)) {
33+
u // u should still be `unknown` here
34+
}
35+
36+
if (u !== 42) {
37+
type A = isTrue<isUnknown<typeof u>>
38+
}
39+
40+
if (u == 42) {
41+
type B = isTrue<isUnknown<typeof u>>
42+
}
43+
44+
if (u == true) {
45+
type C = isTrue<isUnknown<typeof u>>
46+
}
47+
48+
if (u == Object) {
49+
type D = isTrue<isUnknown<typeof u>>
50+
}
51+
52+
53+
//// [unknownType2.js]
54+
"use strict";
55+
var validate = function (x) { return (x === 'yes' || x === 'no') ? x : 'idk'; }; // No error
56+
var u = undefined;
57+
if (u === 5) {
58+
var y = u.toString(10);
59+
}
60+
if (u === true || u === false) {
61+
var someBool = u;
62+
}
63+
if (u === undefined) {
64+
var undef = u;
65+
}
66+
if (u === null) {
67+
var someNull = u;
68+
}
69+
if (u === symb) {
70+
var symbolAlias = u;
71+
}
72+
if (!(u === 42)) {
73+
u; // u should still be `unknown` here
74+
}
75+
if (u !== 42) {
76+
}
77+
if (u == 42) {
78+
}
79+
if (u == true) {
80+
}
81+
if (u == Object) {
82+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
=== tests/cases/conformance/types/unknown/unknownType2.ts ===
2+
type isUnknown<T> = unknown extends T ? true : false;
3+
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
4+
>T : Symbol(T, Decl(unknownType2.ts, 0, 15))
5+
>T : Symbol(T, Decl(unknownType2.ts, 0, 15))
6+
7+
type isTrue<T extends true> = T;
8+
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
9+
>T : Symbol(T, Decl(unknownType2.ts, 1, 12))
10+
>T : Symbol(T, Decl(unknownType2.ts, 1, 12))
11+
12+
type SomeResponse = 'yes' | 'no' | 'idk';
13+
>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32))
14+
15+
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
16+
>validate : Symbol(validate, Decl(unknownType2.ts, 4, 3))
17+
>x : Symbol(x, Decl(unknownType2.ts, 4, 15))
18+
>SomeResponse : Symbol(SomeResponse, Decl(unknownType2.ts, 1, 32))
19+
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
20+
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
21+
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
22+
>x : Symbol(x, Decl(unknownType2.ts, 4, 44))
23+
24+
const u: unknown = undefined;
25+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
26+
>undefined : Symbol(undefined)
27+
28+
declare const symb: unique symbol;
29+
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
30+
31+
if (u === 5) {
32+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
33+
34+
const y = u.toString(10);
35+
>y : Symbol(y, Decl(unknownType2.ts, 11, 9))
36+
>u.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
37+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
38+
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
39+
}
40+
41+
if (u === true || u === false) {
42+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
43+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
44+
45+
const someBool: boolean = u;
46+
>someBool : Symbol(someBool, Decl(unknownType2.ts, 15, 9))
47+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
48+
}
49+
50+
if (u === undefined) {
51+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
52+
>undefined : Symbol(undefined)
53+
54+
const undef: undefined = u;
55+
>undef : Symbol(undef, Decl(unknownType2.ts, 19, 9))
56+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
57+
}
58+
59+
if (u === null) {
60+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
61+
62+
const someNull: null = u;
63+
>someNull : Symbol(someNull, Decl(unknownType2.ts, 23, 9))
64+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
65+
}
66+
67+
if(u === symb) {
68+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
69+
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
70+
71+
const symbolAlias: typeof symb = u;
72+
>symbolAlias : Symbol(symbolAlias, Decl(unknownType2.ts, 27, 9))
73+
>symb : Symbol(symb, Decl(unknownType2.ts, 8, 13))
74+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
75+
}
76+
77+
if (!(u === 42)) {
78+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
79+
80+
u // u should still be `unknown` here
81+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
82+
}
83+
84+
if (u !== 42) {
85+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
86+
87+
type A = isTrue<isUnknown<typeof u>>
88+
>A : Symbol(A, Decl(unknownType2.ts, 34, 15))
89+
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
90+
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
91+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
92+
}
93+
94+
if (u == 42) {
95+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
96+
97+
type B = isTrue<isUnknown<typeof u>>
98+
>B : Symbol(B, Decl(unknownType2.ts, 38, 14))
99+
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
100+
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
101+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
102+
}
103+
104+
if (u == true) {
105+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
106+
107+
type C = isTrue<isUnknown<typeof u>>
108+
>C : Symbol(C, Decl(unknownType2.ts, 42, 16))
109+
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
110+
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
111+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
112+
}
113+
114+
if (u == Object) {
115+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
116+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
117+
118+
type D = isTrue<isUnknown<typeof u>>
119+
>D : Symbol(D, Decl(unknownType2.ts, 46, 18))
120+
>isTrue : Symbol(isTrue, Decl(unknownType2.ts, 0, 53))
121+
>isUnknown : Symbol(isUnknown, Decl(unknownType2.ts, 0, 0))
122+
>u : Symbol(u, Decl(unknownType2.ts, 6, 5))
123+
}
124+
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
=== tests/cases/conformance/types/unknown/unknownType2.ts ===
2+
type isUnknown<T> = unknown extends T ? true : false;
3+
>isUnknown : isUnknown<T>
4+
>true : true
5+
>false : false
6+
7+
type isTrue<T extends true> = T;
8+
>isTrue : T
9+
>true : true
10+
11+
type SomeResponse = 'yes' | 'no' | 'idk';
12+
>SomeResponse : SomeResponse
13+
14+
let validate: (x: unknown) => SomeResponse = x => (x === 'yes' || x === 'no') ? x : 'idk'; // No error
15+
>validate : (x: unknown) => SomeResponse
16+
>x : unknown
17+
>x => (x === 'yes' || x === 'no') ? x : 'idk' : (x: unknown) => "yes" | "no" | "idk"
18+
>x : unknown
19+
>(x === 'yes' || x === 'no') ? x : 'idk' : SomeResponse
20+
>(x === 'yes' || x === 'no') : boolean
21+
>x === 'yes' || x === 'no' : boolean
22+
>x === 'yes' : boolean
23+
>x : unknown
24+
>'yes' : "yes"
25+
>x === 'no' : boolean
26+
>x : unknown
27+
>'no' : "no"
28+
>x : "yes" | "no"
29+
>'idk' : "idk"
30+
31+
const u: unknown = undefined;
32+
>u : unknown
33+
>undefined : undefined
34+
35+
declare const symb: unique symbol;
36+
>symb : unique symbol
37+
38+
if (u === 5) {
39+
>u === 5 : boolean
40+
>u : unknown
41+
>5 : 5
42+
43+
const y = u.toString(10);
44+
>y : string
45+
>u.toString(10) : string
46+
>u.toString : (radix?: number | undefined) => string
47+
>u : 5
48+
>toString : (radix?: number | undefined) => string
49+
>10 : 10
50+
}
51+
52+
if (u === true || u === false) {
53+
>u === true || u === false : boolean
54+
>u === true : boolean
55+
>u : unknown
56+
>true : true
57+
>u === false : boolean
58+
>u : unknown
59+
>false : false
60+
61+
const someBool: boolean = u;
62+
>someBool : boolean
63+
>u : boolean
64+
}
65+
66+
if (u === undefined) {
67+
>u === undefined : boolean
68+
>u : unknown
69+
>undefined : undefined
70+
71+
const undef: undefined = u;
72+
>undef : undefined
73+
>u : undefined
74+
}
75+
76+
if (u === null) {
77+
>u === null : boolean
78+
>u : unknown
79+
>null : null
80+
81+
const someNull: null = u;
82+
>someNull : null
83+
>null : null
84+
>u : null
85+
}
86+
87+
if(u === symb) {
88+
>u === symb : boolean
89+
>u : unknown
90+
>symb : unique symbol
91+
92+
const symbolAlias: typeof symb = u;
93+
>symbolAlias : unique symbol
94+
>symb : unique symbol
95+
>u : unique symbol
96+
}
97+
98+
if (!(u === 42)) {
99+
>!(u === 42) : boolean
100+
>(u === 42) : boolean
101+
>u === 42 : boolean
102+
>u : unknown
103+
>42 : 42
104+
105+
u // u should still be `unknown` here
106+
>u : unknown
107+
}
108+
109+
if (u !== 42) {
110+
>u !== 42 : boolean
111+
>u : unknown
112+
>42 : 42
113+
114+
type A = isTrue<isUnknown<typeof u>>
115+
>A : true
116+
>u : unknown
117+
}
118+
119+
if (u == 42) {
120+
>u == 42 : boolean
121+
>u : unknown
122+
>42 : 42
123+
124+
type B = isTrue<isUnknown<typeof u>>
125+
>B : true
126+
>u : unknown
127+
}
128+
129+
if (u == true) {
130+
>u == true : boolean
131+
>u : unknown
132+
>true : true
133+
134+
type C = isTrue<isUnknown<typeof u>>
135+
>C : true
136+
>u : unknown
137+
}
138+
139+
if (u == Object) {
140+
>u == Object : boolean
141+
>u : unknown
142+
>Object : ObjectConstructor
143+
144+
type D = isTrue<isUnknown<typeof u>>
145+
>D : true
146+
>u : unknown
147+
}
148+

0 commit comments

Comments
 (0)