Merge pull request #11633 from Microsoft/fix-comparable-in-switch

Fix comparable in switch
This commit is contained in:
Nathan Shively-Sanders 2016-11-10 09:20:46 -08:00 committed by GitHub
commit 6398e0d6dd
10 changed files with 117 additions and 20 deletions

View file

@ -16980,6 +16980,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) {
@ -17000,10 +17001,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);

View file

@ -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) ====
@ -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'.
}

View file

@ -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) ====
@ -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;
}
}

View file

@ -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'.
@ -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
@ -30,4 +30,5 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T
case "sss": break;
case 123: break;
case true: break;
}
}

View file

@ -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
}
@ -16,7 +16,8 @@ switch (s) {
case "sss": break;
case 123: break;
case true: break;
}
}
//// [switchCasesExpressionTypeMismatch.js]
var Foo = (function () {
@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
}

View file

@ -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
}
@ -15,4 +15,4 @@ switch (s) {
case "sss": break;
case 123: break;
case true: break;
}
}

View file

@ -0,0 +1,13 @@
class MyBrand
{
private _a: number;
}
function test(strInput: string & MyBrand) {
switch(strInput)
{
case "a":
return 1;
}
return 0;
}