Skip to content

Commit 6487d1f

Browse files
mattmccutchenRyanCavanaugh
authored andcommitted
Remove the rule that replaces a union of literal types with the base type when comparing equality to a type that isn't a union of literal types. (microsoft#27588)
type when comparing equality to a type that isn't a union of literal types. The rule is redundant because a primitive type is already directed- comparable to a value of that primitive type, and it causes errors to be missed when comparing a type parameter _constrained_ by a union of literal types to another union of literal types. The baseline changes look like improvements to me. Fixes microsoft#26758.
1 parent bd17874 commit 6487d1f

12 files changed

+110
-24
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23320,12 +23320,6 @@ namespace ts {
2332023320
case SyntaxKind.ExclamationEqualsToken:
2332123321
case SyntaxKind.EqualsEqualsEqualsToken:
2332223322
case SyntaxKind.ExclamationEqualsEqualsToken:
23323-
const leftIsLiteral = isLiteralType(leftType);
23324-
const rightIsLiteral = isLiteralType(rightType);
23325-
if (!leftIsLiteral || !rightIsLiteral) {
23326-
leftType = leftIsLiteral ? getBaseTypeOfLiteralType(leftType) : leftType;
23327-
rightType = rightIsLiteral ? getBaseTypeOfLiteralType(rightType) : rightType;
23328-
}
2332923323
if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) {
2333023324
reportOperatorError();
2333123325
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This condition will always return 'false' since the types 'T' and '"x"' have no overlap.
2+
3+
4+
==== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts (1 errors) ====
5+
// Test for #26758
6+
7+
function foo<T extends "a" | "b">(t: T) {
8+
t === "a"; // Should be allowed
9+
t === "x"; // Should be error
10+
~~~~~~~~~
11+
!!! error TS2367: This condition will always return 'false' since the types 'T' and '"x"' have no overlap.
12+
}
13+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [compareTypeParameterConstrainedByLiteralToLiteral.ts]
2+
// Test for #26758
3+
4+
function foo<T extends "a" | "b">(t: T) {
5+
t === "a"; // Should be allowed
6+
t === "x"; // Should be error
7+
}
8+
9+
10+
//// [compareTypeParameterConstrainedByLiteralToLiteral.js]
11+
// Test for #26758
12+
function foo(t) {
13+
t === "a"; // Should be allowed
14+
t === "x"; // Should be error
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts ===
2+
// Test for #26758
3+
4+
function foo<T extends "a" | "b">(t: T) {
5+
>foo : Symbol(foo, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 0, 0))
6+
>T : Symbol(T, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 13))
7+
>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34))
8+
>T : Symbol(T, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 13))
9+
10+
t === "a"; // Should be allowed
11+
>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34))
12+
13+
t === "x"; // Should be error
14+
>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34))
15+
}
16+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts ===
2+
// Test for #26758
3+
4+
function foo<T extends "a" | "b">(t: T) {
5+
>foo : <T extends "a" | "b">(t: T) => void
6+
>t : T
7+
8+
t === "a"; // Should be allowed
9+
>t === "a" : boolean
10+
>t : T
11+
>"a" : "a"
12+
13+
t === "x"; // Should be error
14+
>t === "x" : boolean
15+
>t : T
16+
>"x" : "x"
17+
}
18+

tests/baselines/reference/expr.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tests/cases/compiler/expr.ts(87,5): error TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap.
2-
tests/cases/compiler/expr.ts(88,5): error TS2367: This condition will always return 'false' since the types 'number' and 'boolean' have no overlap.
2+
tests/cases/compiler/expr.ts(88,5): error TS2367: This condition will always return 'false' since the types 'number' and 'false' have no overlap.
33
tests/cases/compiler/expr.ts(94,5): error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
4-
tests/cases/compiler/expr.ts(95,5): error TS2367: This condition will always return 'false' since the types 'string' and 'boolean' have no overlap.
4+
tests/cases/compiler/expr.ts(95,5): error TS2367: This condition will always return 'false' since the types 'string' and 'false' have no overlap.
55
tests/cases/compiler/expr.ts(98,5): error TS2367: This condition will always return 'false' since the types 'string' and 'E' have no overlap.
66
tests/cases/compiler/expr.ts(115,5): error TS2367: This condition will always return 'false' since the types 'E' and 'string' have no overlap.
77
tests/cases/compiler/expr.ts(116,5): error TS2367: This condition will always return 'false' since the types 'E' and 'false' have no overlap.
@@ -161,7 +161,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari
161161
!!! error TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap.
162162
n==b;
163163
~~~~
164-
!!! error TS2367: This condition will always return 'false' since the types 'number' and 'boolean' have no overlap.
164+
!!! error TS2367: This condition will always return 'false' since the types 'number' and 'false' have no overlap.
165165
n==i;
166166
n==n;
167167
n==e;
@@ -172,7 +172,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari
172172
!!! error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
173173
s==b;
174174
~~~~
175-
!!! error TS2367: This condition will always return 'false' since the types 'string' and 'boolean' have no overlap.
175+
!!! error TS2367: This condition will always return 'false' since the types 'string' and 'false' have no overlap.
176176
s==i;
177177
s==s;
178178
s==e;

tests/baselines/reference/for-inStatementsArrayErrors.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(4,16): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
22
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(5,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
3-
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(6,9): error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
3+
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(6,9): error TS2367: This condition will always return 'false' since the types 'string' and '1' have no overlap.
44
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(8,16): error TS2339: Property 'unknownProperty' does not exist on type 'string'.
55
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(12,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'i' must be of type 'number', but here has type 'string'.
66
tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(16,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'j' must be of type 'any', but here has type 'string'.
@@ -18,7 +18,7 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.
1818
!!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
1919
if (x === 1) {
2020
~~~~~~~
21-
!!! error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
21+
!!! error TS2367: This condition will always return 'false' since the types 'string' and '1' have no overlap.
2222
}
2323
let a3 = x.unknownProperty;
2424
~~~~~~~~~~~~~~~

tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(3,9): error TS2367: This condition will always return 'false' since the types '"foo"' and '"baz"' have no overlap.
2-
tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
2+
tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2367: This condition will always return 'false' since the types '"foo"' and 'number' have no overlap.
33
tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,19): error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
44

55

@@ -12,7 +12,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparis
1212
var b = "foo" !== ("bar" as "foo");
1313
var c = "foo" == (<number>"bar");
1414
~~~~~~~~~~~~~~~~~~~~~~~~
15-
!!! error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
15+
!!! error TS2367: This condition will always return 'false' since the types '"foo"' and 'number' have no overlap.
1616
~~~~~~~~~~~~~
1717
!!! error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
1818
var d = "foo" === ("bar" as EnhancedString);

tests/baselines/reference/stringLiteralsWithEqualityChecks03.errors.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(16,5): error TS2367: This condition will always return 'false' since the types '"foo"' and '"bar"' have no overlap.
2+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(19,5): error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
3+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(20,5): error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
24
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(25,5): error TS2367: This condition will always return 'true' since the types '"foo"' and '"bar"' have no overlap.
5+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(28,5): error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
6+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(29,5): error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
37

48

5-
==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts (2 errors) ====
9+
==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts (6 errors) ====
610
interface Runnable {
711
isRunning: boolean;
812
}
@@ -24,7 +28,11 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(25,5
2428
b = "bar" === x;
2529
b = x === "bar";
2630
b = y === "bar";
31+
~~~~~~~~~~~
32+
!!! error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
2733
b = "bar" === y;
34+
~~~~~~~~~~~
35+
!!! error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
2836

2937
b = x !== y;
3038
b = "foo" !== y
@@ -35,5 +43,9 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(25,5
3543
b = "bar" !== x;
3644
b = x !== "bar";
3745
b = y !== "bar";
46+
~~~~~~~~~~~
47+
!!! error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
3848
b = "bar" !== y;
49+
~~~~~~~~~~~
50+
!!! error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
3951

tests/baselines/reference/stringLiteralsWithEqualityChecks04.errors.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(16,5): error TS2367: This condition will always return 'false' since the types '"foo"' and '"bar"' have no overlap.
2+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(19,5): error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
3+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(20,5): error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
24
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(25,5): error TS2367: This condition will always return 'true' since the types '"foo"' and '"bar"' have no overlap.
5+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(28,5): error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
6+
tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(29,5): error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
37

48

5-
==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts (2 errors) ====
9+
==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts (6 errors) ====
610
interface Runnable {
711
isRunning: boolean;
812
}
@@ -24,7 +28,11 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(25,5
2428
b = "bar" == x;
2529
b = x == "bar";
2630
b = y == "bar";
31+
~~~~~~~~~~
32+
!!! error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
2733
b = "bar" == y;
34+
~~~~~~~~~~
35+
!!! error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
2836

2937
b = x != y;
3038
b = "foo" != y
@@ -35,5 +43,9 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(25,5
3543
b = "bar" != x;
3644
b = x != "bar";
3745
b = y != "bar";
46+
~~~~~~~~~~
47+
!!! error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap.
3848
b = "bar" != y;
49+
~~~~~~~~~~
50+
!!! error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap.
3951

0 commit comments

Comments
 (0)