Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix comparable in switch #11633

Merged
merged 10 commits into from
Nov 10, 2016
13 changes: 10 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16883,6 +16883,7 @@ namespace ts {
let hasDuplicateDefaultClause = false;

const expressionType = checkExpression(node.expression);
const expressionIsLiteral = isLiteralType(expressionType);
forEach(node.caseBlock.clauses, clause => {
// Grammar check for duplicate default clauses, skip if we already report duplicate default clause
if (clause.kind === SyntaxKind.DefaultClause && !hasDuplicateDefaultClause) {
Expand All @@ -16903,10 +16904,16 @@ namespace ts {
// TypeScript 1.0 spec (April 2014): 5.9
// In a 'switch' statement, each 'case' expression must be of a type that is comparable
// to or from the type of the 'switch' expression.
const caseType = checkExpression(caseClause.expression);
if (!isTypeEqualityComparableTo(expressionType, caseType)) {
let caseType = checkExpression(caseClause.expression);
const caseIsLiteral = isLiteralType(caseType);
let comparedExpressionType = expressionType;
if (!caseIsLiteral || !expressionIsLiteral) {
caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType;
comparedExpressionType = getBaseTypeOfLiteralType(expressionType);
}
if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) {
// expressionType is not comparable to caseType, try the reversed check and report errors if it fails
checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined);
checkTypeComparableTo(caseType, comparedExpressionType, caseClause.expression, /*headMessage*/ undefined);
}
}
forEach(clause.statements, checkSourceElement);
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/switchAssignmentCompat.errors.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'.
tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type 'number'.


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

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
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"'.
Type '{ a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
Type '{ a: "C"; e: any; }' is not comparable to type '"C"'.
tests/cases/compiler/switchCaseCircularRefeference.ts(5,10): error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type 'string'.
Type '{ a: "C"; e: any; }' is not comparable to type 'string'.


==== tests/cases/compiler/switchCaseCircularRefeference.ts (1 errors) ====
Expand All @@ -10,9 +9,8 @@ tests/cases/compiler/switchCaseCircularRefeference.ts(5,10): error TS2678: Type
switch (x.a) {
case x:
~
!!! error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type '"A" | "C"'.
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type '"C"'.
!!! error TS2678: Type '{ a: "A"; b: any; } | { a: "C"; e: any; }' is not comparable to type 'string'.
!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type 'string'.
break;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'.
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(5,10): error TS2678: Type '"sss"' is not comparable to type '0'.
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(6,10): error TS2678: Type '123' is not comparable to type '0'.
tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'true' is not comparable to type '0'.
Expand All @@ -10,11 +10,11 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T
switch (0) {
case Foo: break; // Error
~~~
!!! error TS2678: Type 'typeof Foo' is not comparable to type '0'.
!!! error TS2678: Type 'typeof Foo' is not comparable to type 'number'.
case "sss": break; // Error
~~~~~
!!! error TS2678: Type '"sss"' is not comparable to type '0'.
case 123: break; // No Error
case 123: break; // Error
~~~
!!! error TS2678: Type '123' is not comparable to type '0'.
case true: break; // Error
Expand All @@ -30,4 +30,5 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T
case "sss": break;
case 123: break;
case true: break;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Foo { }
switch (0) {
case Foo: break; // Error
case "sss": break; // Error
case 123: break; // No Error
case 123: break; // Error
case true: break; // Error
}

Expand All @@ -16,7 +16,8 @@ switch (s) {
case "sss": break;
case 123: break;
case true: break;
}
}


//// [switchCasesExpressionTypeMismatch.js]
var Foo = (function () {
Expand All @@ -27,7 +28,7 @@ var Foo = (function () {
switch (0) {
case Foo: break; // Error
case "sss": break; // Error
case 123: break; // No Error
case 123: break; // Error
case true: break; // Error
}
var s = 0;
Expand Down
29 changes: 29 additions & 0 deletions tests/baselines/reference/switchComparableCompatForBrands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [switchComparableCompatForBrands.ts]
class MyBrand
{
private _a: number;
}

function test(strInput: string & MyBrand) {
switch(strInput)
{
case "a":
return 1;
}
return 0;
}


//// [switchComparableCompatForBrands.js]
var MyBrand = (function () {
function MyBrand() {
}
return MyBrand;
}());
function test(strInput) {
switch (strInput) {
case "a":
return 1;
}
return 0;
}
22 changes: 22 additions & 0 deletions tests/baselines/reference/switchComparableCompatForBrands.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/compiler/switchComparableCompatForBrands.ts ===
class MyBrand
>MyBrand : Symbol(MyBrand, Decl(switchComparableCompatForBrands.ts, 0, 0))
{
private _a: number;
>_a : Symbol(MyBrand._a, Decl(switchComparableCompatForBrands.ts, 1, 1))
}

function test(strInput: string & MyBrand) {
>test : Symbol(test, Decl(switchComparableCompatForBrands.ts, 3, 1))
>strInput : Symbol(strInput, Decl(switchComparableCompatForBrands.ts, 5, 14))
>MyBrand : Symbol(MyBrand, Decl(switchComparableCompatForBrands.ts, 0, 0))

switch(strInput)
>strInput : Symbol(strInput, Decl(switchComparableCompatForBrands.ts, 5, 14))
{
case "a":
return 1;
}
return 0;
}

26 changes: 26 additions & 0 deletions tests/baselines/reference/switchComparableCompatForBrands.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/switchComparableCompatForBrands.ts ===
class MyBrand
>MyBrand : MyBrand
{
private _a: number;
>_a : number
}

function test(strInput: string & MyBrand) {
>test : (strInput: string & MyBrand) => 1 | 0
>strInput : string & MyBrand
>MyBrand : MyBrand

switch(strInput)
>strInput : string & MyBrand
{
case "a":
>"a" : "a"

return 1;
>1 : 1
}
return 0;
>0 : 0
}

4 changes: 2 additions & 2 deletions tests/cases/compiler/switchCasesExpressionTypeMismatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Foo { }
switch (0) {
case Foo: break; // Error
case "sss": break; // Error
case 123: break; // No Error
case 123: break; // Error
case true: break; // Error
}

Expand All @@ -15,4 +15,4 @@ switch (s) {
case "sss": break;
case 123: break;
case true: break;
}
}
13 changes: 13 additions & 0 deletions tests/cases/compiler/switchComparableCompatForBrands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class MyBrand
{
private _a: number;
}

function test(strInput: string & MyBrand) {
switch(strInput)
{
case "a":
return 1;
}
return 0;
}