Skip to content

Commit 6398e0d

Browse files
authored
Merge pull request #11633 from Microsoft/fix-comparable-in-switch
Fix comparable in switch
2 parents 28cc938 + 1866d05 commit 6398e0d

10 files changed

+117
-20
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16980,6 +16980,7 @@ namespace ts {
1698016980
let hasDuplicateDefaultClause = false;
1698116981

1698216982
const expressionType = checkExpression(node.expression);
16983+
const expressionIsLiteral = isLiteralType(expressionType);
1698316984
forEach(node.caseBlock.clauses, clause => {
1698416985
// Grammar check for duplicate default clauses, skip if we already report duplicate default clause
1698516986
if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) {
@@ -17000,10 +17001,16 @@ namespace ts {
1700017001
// TypeScript 1.0 spec (April 2014): 5.9
1700117002
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
1700217003
// to or from the type of the 'switch' expression.
17003-
const caseType = checkExpression(caseClause.expression);
17004-
if (!isTypeEqualityComparableTo(expressionType, caseType)) {
17004+
let caseType = checkExpression(caseClause.expression);
17005+
const caseIsLiteral = isLiteralType(caseType);
17006+
let comparedExpressionType = expressionType;
17007+
if (!caseIsLiteral || !expressionIsLiteral) {
17008+
caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType;
17009+
comparedExpressionType = getBaseTypeOfLiteralType(expressionType);
17010+
}
17011+
if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) {
1700517012
// expressionType is not comparable to caseType, try the reversed check and report errors if it fails
17006-
checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined);
17013+
checkTypeComparableTo(caseType, comparedExpressionType, caseClause.expression, /*headMessage*/ undefined);
1700717014
}
1700817015
}
1700917016
forEach(clause.statements, checkSourceElement);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'.
1+
tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
22

33

44
==== tests/cases/compiler/switchAssignmentCompat.ts (1 errors) ====
@@ -7,6 +7,6 @@ tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof
77
switch (0) {
88
case Foo: break; // Error expected
99
~~~
10-
!!! error TS2678: Type 'typeof Foo' is not comparable to type '0'.
10+
!!! error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
1111
}
1212

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
tests/cases/compiler/switchCaseCircularRefeference.ts(5,10): error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
2-
Type '{ a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
3-
Type '{ a: "C"; e: any; }' is not comparable to type '"C"'.
1+
tests/cases/compiler/switchCaseCircularRefeference.ts(5,10): error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type 'string'.
2+
Type '{ a: "C"; e: any; }' is not comparable to type 'string'.
43

54

65
==== tests/cases/compiler/switchCaseCircularRefeference.ts (1 errors) ====
@@ -10,9 +9,8 @@ tests/cases/compiler/switchCaseCircularRefeference.ts(5,10): error TS2678: Type
109
switch (x.a) {
1110
case x:
1211
~
13-
!!! error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
14-
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
15-
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type '"C"'.
12+
!!! error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type 'string'.
13+
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type 'string'.
1614
break;
1715
}
1816
}

tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'.
1+
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
22
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(5,10): error TS2678: Type '"sss"' is not comparable to type '0'.
33
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(6,10): error TS2678: Type '123' is not comparable to type '0'.
44
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'true' is not comparable to type '0'.
@@ -10,11 +10,11 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T
1010
switch (0) {
1111
case Foo: break; // Error
1212
~~~
13-
!!! error TS2678: Type 'typeof Foo' is not comparable to type '0'.
13+
!!! error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
1414
case "sss": break; // Error
1515
~~~~~
1616
!!! error TS2678: Type '"sss"' is not comparable to type '0'.
17-
case 123: break; // No Error
17+
case 123: break; // Error
1818
~~~
1919
!!! error TS2678: Type '123' is not comparable to type '0'.
2020
case true: break; // Error
@@ -30,4 +30,5 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T
3030
case "sss": break;
3131
case 123: break;
3232
case true: break;
33-
}
33+
}
34+

tests/baselines/reference/switchCasesExpressionTypeMismatch.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class Foo { }
44
switch (0) {
55
case Foo: break; // Error
66
case "sss": break; // Error
7-
case 123: break; // No Error
7+
case 123: break; // Error
88
case true: break; // Error
99
}
1010

@@ -16,7 +16,8 @@ switch (s) {
1616
case "sss": break;
1717
case 123: break;
1818
case true: break;
19-
}
19+
}
20+
2021

2122
//// [switchCasesExpressionTypeMismatch.js]
2223
var Foo = (function () {
@@ -27,7 +28,7 @@ var Foo = (function () {
2728
switch (0) {
2829
case Foo: break; // Error
2930
case "sss": break; // Error
30-
case 123: break; // No Error
31+
case 123: break; // Error
3132
case true: break; // Error
3233
}
3334
var s = 0;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [switchComparableCompatForBrands.ts]
2+
class MyBrand
3+
{
4+
private _a: number;
5+
}
6+
7+
function test(strInput: string & MyBrand) {
8+
switch(strInput)
9+
{
10+
case "a":
11+
return 1;
12+
}
13+
return 0;
14+
}
15+
16+
17+
//// [switchComparableCompatForBrands.js]
18+
var MyBrand = (function () {
19+
function MyBrand() {
20+
}
21+
return MyBrand;
22+
}());
23+
function test(strInput) {
24+
switch (strInput) {
25+
case "a":
26+
return 1;
27+
}
28+
return 0;
29+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/switchComparableCompatForBrands.ts ===
2+
class MyBrand
3+
>MyBrand : Symbol(MyBrand, Decl(switchComparableCompatForBrands.ts, 0, 0))
4+
{
5+
private _a: number;
6+
>_a : Symbol(MyBrand._a, Decl(switchComparableCompatForBrands.ts, 1, 1))
7+
}
8+
9+
function test(strInput: string & MyBrand) {
10+
>test : Symbol(test, Decl(switchComparableCompatForBrands.ts, 3, 1))
11+
>strInput : Symbol(strInput, Decl(switchComparableCompatForBrands.ts, 5, 14))
12+
>MyBrand : Symbol(MyBrand, Decl(switchComparableCompatForBrands.ts, 0, 0))
13+
14+
switch(strInput)
15+
>strInput : Symbol(strInput, Decl(switchComparableCompatForBrands.ts, 5, 14))
16+
{
17+
case "a":
18+
return 1;
19+
}
20+
return 0;
21+
}
22+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/compiler/switchComparableCompatForBrands.ts ===
2+
class MyBrand
3+
>MyBrand : MyBrand
4+
{
5+
private _a: number;
6+
>_a : number
7+
}
8+
9+
function test(strInput: string & MyBrand) {
10+
>test : (strInput: string & MyBrand) => 1 | 0
11+
>strInput : string & MyBrand
12+
>MyBrand : MyBrand
13+
14+
switch(strInput)
15+
>strInput : string & MyBrand
16+
{
17+
case "a":
18+
>"a" : "a"
19+
20+
return 1;
21+
>1 : 1
22+
}
23+
return 0;
24+
>0 : 0
25+
}
26+

tests/cases/compiler/switchCasesExpressionTypeMismatch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class Foo { }
33
switch (0) {
44
case Foo: break; // Error
55
case "sss": break; // Error
6-
case 123: break; // No Error
6+
case 123: break; // Error
77
case true: break; // Error
88
}
99

@@ -15,4 +15,4 @@ switch (s) {
1515
case "sss": break;
1616
case 123: break;
1717
case true: break;
18-
}
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class MyBrand
2+
{
3+
private _a: number;
4+
}
5+
6+
function test(strInput: string & MyBrand) {
7+
switch(strInput)
8+
{
9+
case "a":
10+
return 1;
11+
}
12+
return 0;
13+
}

0 commit comments

Comments
 (0)