Skip to content

Commit ee33225

Browse files
committed
add check for >, >=, <, <= for unconstrained types
1 parent e450c46 commit ee33225

File tree

5 files changed

+518
-2
lines changed

5 files changed

+518
-2
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27493,6 +27493,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2749327493
return type === unknownUnionType ? unknownType : type;
2749427494
}
2749527495

27496+
// use to determine if a parameter may be undefined or null (or is unknown/unconstrained)
27497+
function getUnknownIfMaybeUnknown(type: Type) {
27498+
return (strictNullChecks && type.flags & TypeFlags.Instantiable) ? getBaseConstraintOfType(type) || unknownType : type;
27499+
}
27500+
2749627501
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
2749727502
return defaultExpression ?
2749827503
getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) :
@@ -39677,8 +39682,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3967739682
case SyntaxKind.LessThanEqualsToken:
3967839683
case SyntaxKind.GreaterThanEqualsToken:
3967939684
if (checkForDisallowedESSymbolOperand(operator)) {
39680-
leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(leftType, left));
39681-
rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(rightType, right));
39685+
39686+
leftType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(leftType), left));
39687+
rightType = getBaseTypeOfLiteralTypeForComparison(checkNonNullType(getUnknownIfMaybeUnknown(rightType), right));
3968239688
reportOperatorErrorUnless((left, right) => {
3968339689
if (isTypeAny(left) || isTypeAny(right)) {
3968439690
return true;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
unconstrainedTypeComparison.ts(2,12): error TS18046: 'a' is of type 'unknown'.
2+
unconstrainedTypeComparison.ts(2,16): error TS18046: 'b' is of type 'unknown'.
3+
unconstrainedTypeComparison.ts(6,12): error TS18049: 'a' is possibly 'null' or 'undefined'.
4+
unconstrainedTypeComparison.ts(6,16): error TS18049: 'b' is possibly 'null' or 'undefined'.
5+
unconstrainedTypeComparison.ts(10,12): error TS18046: 'a' is of type 'unknown'.
6+
unconstrainedTypeComparison.ts(10,16): error TS18046: 'b' is of type 'unknown'.
7+
unconstrainedTypeComparison.ts(14,12): error TS18046: 'a' is of type 'unknown'.
8+
unconstrainedTypeComparison.ts(14,16): error TS18046: 'b' is of type 'unknown'.
9+
unconstrainedTypeComparison.ts(18,12): error TS18049: 'a' is possibly 'null' or 'undefined'.
10+
unconstrainedTypeComparison.ts(18,16): error TS18049: 'b' is possibly 'null' or 'undefined'.
11+
unconstrainedTypeComparison.ts(22,12): error TS18046: 'a' is of type 'unknown'.
12+
unconstrainedTypeComparison.ts(22,16): error TS18046: 'b' is of type 'unknown'.
13+
unconstrainedTypeComparison.ts(26,12): error TS18048: 'a' is possibly 'undefined'.
14+
unconstrainedTypeComparison.ts(26,16): error TS18048: 'b' is possibly 'undefined'.
15+
unconstrainedTypeComparison.ts(30,12): error TS18047: 'a' is possibly 'null'.
16+
unconstrainedTypeComparison.ts(30,16): error TS18047: 'b' is possibly 'null'.
17+
unconstrainedTypeComparison.ts(34,12): error TS18049: 'a' is possibly 'null' or 'undefined'.
18+
unconstrainedTypeComparison.ts(34,16): error TS18049: 'b' is possibly 'null' or 'undefined'.
19+
unconstrainedTypeComparison.ts(45,12): error TS18047: 'a' is possibly 'null'.
20+
unconstrainedTypeComparison.ts(45,16): error TS18048: 'b' is possibly 'undefined'.
21+
22+
23+
==== unconstrainedTypeComparison.ts (20 errors) ====
24+
function f1<T>(a: T, b: T): boolean {
25+
return a > b;
26+
~
27+
!!! error TS18046: 'a' is of type 'unknown'.
28+
~
29+
!!! error TS18046: 'b' is of type 'unknown'.
30+
}
31+
32+
function f2<T extends {} | undefined | null>(a: T, b: T): boolean {
33+
return a > b;
34+
~
35+
!!! error TS18049: 'a' is possibly 'null' or 'undefined'.
36+
~
37+
!!! error TS18049: 'b' is possibly 'null' or 'undefined'.
38+
}
39+
40+
function f3<T extends unknown>(a: T, b: T): boolean {
41+
return a > b;
42+
~
43+
!!! error TS18046: 'a' is of type 'unknown'.
44+
~
45+
!!! error TS18046: 'b' is of type 'unknown'.
46+
}
47+
48+
function f4<T, U extends T>(a: U, b: U): boolean {
49+
return a > b;
50+
~
51+
!!! error TS18046: 'a' is of type 'unknown'.
52+
~
53+
!!! error TS18046: 'b' is of type 'unknown'.
54+
}
55+
56+
function f5<T extends {} | undefined | null, U extends T>(a: U, b: U): boolean {
57+
return a > b;
58+
~
59+
!!! error TS18049: 'a' is possibly 'null' or 'undefined'.
60+
~
61+
!!! error TS18049: 'b' is possibly 'null' or 'undefined'.
62+
}
63+
64+
function f6<T extends unknown, U extends T>(a: U, b: U): boolean {
65+
return a > b;
66+
~
67+
!!! error TS18046: 'a' is of type 'unknown'.
68+
~
69+
!!! error TS18046: 'b' is of type 'unknown'.
70+
}
71+
72+
function f7<T extends {} | undefined, U extends T>(a: U, b: U): boolean {
73+
return a > b;
74+
~
75+
!!! error TS18048: 'a' is possibly 'undefined'.
76+
~
77+
!!! error TS18048: 'b' is possibly 'undefined'.
78+
}
79+
80+
function f8<T extends {} | null, U extends T>(a: U, b: U): boolean {
81+
return a > b;
82+
~
83+
!!! error TS18047: 'a' is possibly 'null'.
84+
~
85+
!!! error TS18047: 'b' is possibly 'null'.
86+
}
87+
88+
function f9<T extends undefined | null, U extends T>(a: U, b: U): boolean {
89+
return a > b;
90+
~
91+
!!! error TS18049: 'a' is possibly 'null' or 'undefined'.
92+
~
93+
!!! error TS18049: 'b' is possibly 'null' or 'undefined'.
94+
}
95+
96+
97+
function compare<T>(a: T, b: T): boolean {
98+
if (a === undefined) {
99+
return false;
100+
}
101+
if (b === null) {
102+
return false;
103+
}
104+
return a > b;
105+
~
106+
!!! error TS18047: 'a' is possibly 'null'.
107+
~
108+
!!! error TS18048: 'b' is possibly 'undefined'.
109+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
//// [tests/cases/compiler/unconstrainedTypeComparison.ts] ////
2+
3+
=== unconstrainedTypeComparison.ts ===
4+
function f1<T>(a: T, b: T): boolean {
5+
>f1 : Symbol(f1, Decl(unconstrainedTypeComparison.ts, 0, 0))
6+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12))
7+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 0, 15))
8+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12))
9+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 0, 20))
10+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 0, 12))
11+
12+
return a > b;
13+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 0, 15))
14+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 0, 20))
15+
}
16+
17+
function f2<T extends {} | undefined | null>(a: T, b: T): boolean {
18+
>f2 : Symbol(f2, Decl(unconstrainedTypeComparison.ts, 2, 1))
19+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12))
20+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 4, 45))
21+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12))
22+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 4, 50))
23+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 4, 12))
24+
25+
return a > b;
26+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 4, 45))
27+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 4, 50))
28+
}
29+
30+
function f3<T extends unknown>(a: T, b: T): boolean {
31+
>f3 : Symbol(f3, Decl(unconstrainedTypeComparison.ts, 6, 1))
32+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12))
33+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 8, 31))
34+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12))
35+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 8, 36))
36+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 8, 12))
37+
38+
return a > b;
39+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 8, 31))
40+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 8, 36))
41+
}
42+
43+
function f4<T, U extends T>(a: U, b: U): boolean {
44+
>f4 : Symbol(f4, Decl(unconstrainedTypeComparison.ts, 10, 1))
45+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 12, 12))
46+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14))
47+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 12, 12))
48+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 12, 28))
49+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14))
50+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 12, 33))
51+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 12, 14))
52+
53+
return a > b;
54+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 12, 28))
55+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 12, 33))
56+
}
57+
58+
function f5<T extends {} | undefined | null, U extends T>(a: U, b: U): boolean {
59+
>f5 : Symbol(f5, Decl(unconstrainedTypeComparison.ts, 14, 1))
60+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 16, 12))
61+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44))
62+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 16, 12))
63+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 16, 58))
64+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44))
65+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 16, 63))
66+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 16, 44))
67+
68+
return a > b;
69+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 16, 58))
70+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 16, 63))
71+
}
72+
73+
function f6<T extends unknown, U extends T>(a: U, b: U): boolean {
74+
>f6 : Symbol(f6, Decl(unconstrainedTypeComparison.ts, 18, 1))
75+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 20, 12))
76+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30))
77+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 20, 12))
78+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 20, 44))
79+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30))
80+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 20, 49))
81+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 20, 30))
82+
83+
return a > b;
84+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 20, 44))
85+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 20, 49))
86+
}
87+
88+
function f7<T extends {} | undefined, U extends T>(a: U, b: U): boolean {
89+
>f7 : Symbol(f7, Decl(unconstrainedTypeComparison.ts, 22, 1))
90+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 24, 12))
91+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37))
92+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 24, 12))
93+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 24, 51))
94+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37))
95+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 24, 56))
96+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 24, 37))
97+
98+
return a > b;
99+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 24, 51))
100+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 24, 56))
101+
}
102+
103+
function f8<T extends {} | null, U extends T>(a: U, b: U): boolean {
104+
>f8 : Symbol(f8, Decl(unconstrainedTypeComparison.ts, 26, 1))
105+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 28, 12))
106+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32))
107+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 28, 12))
108+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 28, 46))
109+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32))
110+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 28, 51))
111+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 28, 32))
112+
113+
return a > b;
114+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 28, 46))
115+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 28, 51))
116+
}
117+
118+
function f9<T extends undefined | null, U extends T>(a: U, b: U): boolean {
119+
>f9 : Symbol(f9, Decl(unconstrainedTypeComparison.ts, 30, 1))
120+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 32, 12))
121+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39))
122+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 32, 12))
123+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 32, 53))
124+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39))
125+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 32, 58))
126+
>U : Symbol(U, Decl(unconstrainedTypeComparison.ts, 32, 39))
127+
128+
return a > b;
129+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 32, 53))
130+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 32, 58))
131+
}
132+
133+
134+
function compare<T>(a: T, b: T): boolean {
135+
>compare : Symbol(compare, Decl(unconstrainedTypeComparison.ts, 34, 1))
136+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17))
137+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20))
138+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17))
139+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25))
140+
>T : Symbol(T, Decl(unconstrainedTypeComparison.ts, 37, 17))
141+
142+
if (a === undefined) {
143+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20))
144+
>undefined : Symbol(undefined)
145+
146+
return false;
147+
}
148+
if (b === null) {
149+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25))
150+
151+
return false;
152+
}
153+
return a > b;
154+
>a : Symbol(a, Decl(unconstrainedTypeComparison.ts, 37, 20))
155+
>b : Symbol(b, Decl(unconstrainedTypeComparison.ts, 37, 25))
156+
}

0 commit comments

Comments
 (0)