Skip to content

Commit 0df89cc

Browse files
Merge pull request #26941 from jack-williams/narrow-unknown-with-triple-equals
Fix #25172: Add narrowing for `unknown` with triple equals
2 parents ea74ed4 + 6f30537 commit 0df89cc

File tree

5 files changed

+936
-0
lines changed

5 files changed

+936
-0
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16057,6 +16057,15 @@ namespace ts {
1605716057
assumeTrue = !assumeTrue;
1605816058
}
1605916059
const valueType = getTypeOfExpression(value);
16060+
if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && assumeTrue) {
16061+
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
16062+
return valueType;
16063+
}
16064+
if (valueType.flags & TypeFlags.Object) {
16065+
return nonPrimitiveType;
16066+
}
16067+
return type;
16068+
}
1606016069
if (valueType.flags & TypeFlags.Nullable) {
1606116070
if (!strictNullChecks) {
1606216071
return type;
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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+
type A = isTrue<isUnknown<typeof u>>
34+
}
35+
36+
if (u !== 42) {
37+
type B = isTrue<isUnknown<typeof u>>
38+
}
39+
40+
if (u == 42) {
41+
type C = isTrue<isUnknown<typeof u>>
42+
}
43+
44+
if (u == true) {
45+
type D = isTrue<isUnknown<typeof u>>
46+
}
47+
48+
if (u == Object) {
49+
type E = isTrue<isUnknown<typeof u>>
50+
}
51+
52+
declare const aString: string;
53+
declare const aBoolean: boolean;
54+
declare const aNumber: number;
55+
declare const anObject: object;
56+
declare const anObjectLiteral: { x: number };
57+
declare const aUnion: { x: number } | { y: string };
58+
declare const anIntersection: { x: number } & { y: string };
59+
declare const aFunction: () => number;
60+
61+
if (u === aString) {
62+
let uString: string = u;
63+
}
64+
65+
if (u === aBoolean) {
66+
let uString: boolean = u;
67+
}
68+
69+
if (u === aNumber) {
70+
let uNumber: number = u;
71+
}
72+
73+
if (u === anObject) {
74+
let uObject: object = u;
75+
}
76+
77+
if (u === anObjectLiteral) {
78+
let uObjectLiteral: object = u;
79+
}
80+
81+
if (u === aUnion) {
82+
type unionDoesNotNarrow = isTrue<isUnknown<typeof u>>
83+
}
84+
85+
if (u === anIntersection) {
86+
type intersectionDoesNotNarrow = isTrue<isUnknown<typeof u>>
87+
}
88+
89+
if (u === aFunction) {
90+
let uFunction: object = u;
91+
}
92+
93+
enum NumberEnum {
94+
A,
95+
B,
96+
C
97+
}
98+
99+
enum StringEnum {
100+
A = "A",
101+
B = "B",
102+
C = "C"
103+
}
104+
105+
if (u === NumberEnum || u === StringEnum) {
106+
let enumObj: object = u;
107+
}
108+
109+
if(u === NumberEnum.A) {
110+
let a: NumberEnum.A = u
111+
}
112+
113+
if(u === StringEnum.B) {
114+
let b: StringEnum.B = u
115+
}
116+
117+
118+
//// [unknownType2.js]
119+
"use strict";
120+
var validate = function (x) { return (x === 'yes' || x === 'no') ? x : 'idk'; }; // No error
121+
var u = undefined;
122+
if (u === 5) {
123+
var y = u.toString(10);
124+
}
125+
if (u === true || u === false) {
126+
var someBool = u;
127+
}
128+
if (u === undefined) {
129+
var undef = u;
130+
}
131+
if (u === null) {
132+
var someNull = u;
133+
}
134+
if (u === symb) {
135+
var symbolAlias = u;
136+
}
137+
if (!(u === 42)) {
138+
}
139+
if (u !== 42) {
140+
}
141+
if (u == 42) {
142+
}
143+
if (u == true) {
144+
}
145+
if (u == Object) {
146+
}
147+
if (u === aString) {
148+
var uString = u;
149+
}
150+
if (u === aBoolean) {
151+
var uString = u;
152+
}
153+
if (u === aNumber) {
154+
var uNumber = u;
155+
}
156+
if (u === anObject) {
157+
var uObject = u;
158+
}
159+
if (u === anObjectLiteral) {
160+
var uObjectLiteral = u;
161+
}
162+
if (u === aUnion) {
163+
}
164+
if (u === anIntersection) {
165+
}
166+
if (u === aFunction) {
167+
var uFunction = u;
168+
}
169+
var NumberEnum;
170+
(function (NumberEnum) {
171+
NumberEnum[NumberEnum["A"] = 0] = "A";
172+
NumberEnum[NumberEnum["B"] = 1] = "B";
173+
NumberEnum[NumberEnum["C"] = 2] = "C";
174+
})(NumberEnum || (NumberEnum = {}));
175+
var StringEnum;
176+
(function (StringEnum) {
177+
StringEnum["A"] = "A";
178+
StringEnum["B"] = "B";
179+
StringEnum["C"] = "C";
180+
})(StringEnum || (StringEnum = {}));
181+
if (u === NumberEnum || u === StringEnum) {
182+
var enumObj = u;
183+
}
184+
if (u === NumberEnum.A) {
185+
var a = u;
186+
}
187+
if (u === StringEnum.B) {
188+
var b = u;
189+
}

0 commit comments

Comments
 (0)