From 00ebf59b500d064524af5ddfef87b6c45b53e655 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 13 Oct 2016 16:26:44 -0700 Subject: [PATCH 1/9] Use same literal comparison rules for switch/=== switch was missing the rule for converting literal types to the literal base type if needed. --- src/compiler/checker.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30a0bbd2042c8..6df09cca70493 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16584,7 +16584,7 @@ namespace ts { let firstDefaultClause: CaseOrDefaultClause; let hasDuplicateDefaultClause = false; - const expressionType = checkExpression(node.expression); + let expressionType = checkExpression(node.expression); 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) { @@ -16605,7 +16605,13 @@ 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); + let caseType = checkExpression(caseClause.expression); + const expressionIsLiteral = isLiteralType(expressionType); + const caseIsLiteral = isLiteralType(caseType); + if (!expressionIsLiteral || !caseIsLiteral) { + expressionType = expressionIsLiteral ? getBaseTypeOfLiteralType(expressionType) : expressionType; + caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; + } if (!isTypeEqualityComparableTo(expressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined); From 982abc29442fc145e9364aa7fad510c15f1008ac Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 13 Oct 2016 16:29:35 -0700 Subject: [PATCH 2/9] Add switch comparability test and update baselines --- .../switchAssignmentCompat.errors.txt | 4 +-- .../switchCaseCircularRefeference.errors.txt | 10 +++---- ...itchCasesExpressionTypeMismatch.errors.txt | 17 +++++------ .../switchComparableCompatForBrands.js | 29 +++++++++++++++++++ .../switchComparableCompatForBrands.symbols | 22 ++++++++++++++ .../switchComparableCompatForBrands.types | 26 +++++++++++++++++ .../switchComparableCompatForBrands.ts | 13 +++++++++ 7 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 tests/baselines/reference/switchComparableCompatForBrands.js create mode 100644 tests/baselines/reference/switchComparableCompatForBrands.symbols create mode 100644 tests/baselines/reference/switchComparableCompatForBrands.types create mode 100644 tests/cases/compiler/switchComparableCompatForBrands.ts diff --git a/tests/baselines/reference/switchAssignmentCompat.errors.txt b/tests/baselines/reference/switchAssignmentCompat.errors.txt index 46c89fa5b52d2..747a1035b6846 100644 --- a/tests/baselines/reference/switchAssignmentCompat.errors.txt +++ b/tests/baselines/reference/switchAssignmentCompat.errors.txt @@ -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'. } \ No newline at end of file diff --git a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt index 9ee571de468ce..bc576f8e723b8 100644 --- a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt +++ b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt @@ -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; } } \ No newline at end of file diff --git a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt index 3ea12e2460f65..26d39efef48ec 100644 --- a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt +++ b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt @@ -1,25 +1,22 @@ -tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'. -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'. +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 'string' is not comparable to type 'number'. +tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'boolean' is not comparable to type 'number'. -==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (4 errors) ==== +==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (3 errors) ==== class Foo { } 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'. +!!! error TS2678: Type 'string' is not comparable to type 'number'. case 123: break; // No Error - ~~~ -!!! error TS2678: Type '123' is not comparable to type '0'. case true: break; // Error ~~~~ -!!! error TS2678: Type 'true' is not comparable to type '0'. +!!! error TS2678: Type 'boolean' is not comparable to type 'number'. } var s: any = 0; diff --git a/tests/baselines/reference/switchComparableCompatForBrands.js b/tests/baselines/reference/switchComparableCompatForBrands.js new file mode 100644 index 0000000000000..316edfc9c31b1 --- /dev/null +++ b/tests/baselines/reference/switchComparableCompatForBrands.js @@ -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; +} diff --git a/tests/baselines/reference/switchComparableCompatForBrands.symbols b/tests/baselines/reference/switchComparableCompatForBrands.symbols new file mode 100644 index 0000000000000..adf6a5174adf3 --- /dev/null +++ b/tests/baselines/reference/switchComparableCompatForBrands.symbols @@ -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; +} + diff --git a/tests/baselines/reference/switchComparableCompatForBrands.types b/tests/baselines/reference/switchComparableCompatForBrands.types new file mode 100644 index 0000000000000..5e479ee29dc2e --- /dev/null +++ b/tests/baselines/reference/switchComparableCompatForBrands.types @@ -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 +} + diff --git a/tests/cases/compiler/switchComparableCompatForBrands.ts b/tests/cases/compiler/switchComparableCompatForBrands.ts new file mode 100644 index 0000000000000..4b90db40e1db8 --- /dev/null +++ b/tests/cases/compiler/switchComparableCompatForBrands.ts @@ -0,0 +1,13 @@ +class MyBrand +{ + private _a: number; +} + +function test(strInput: string & MyBrand) { + switch(strInput) + { + case "a": + return 1; + } + return 0; +} From 0168f67f63c1180caca7c1d3500773e64580d952 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 14 Oct 2016 08:55:01 -0700 Subject: [PATCH 3/9] Push eitherIsNotLiteral check into isTypeEqualityComparableTo Since all callers need this check -- it's how equality is supposed to work everywhere. --- src/compiler/checker.ts | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6df09cca70493..24f71ee41ed13 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13532,6 +13532,12 @@ namespace ts { } function isTypeEqualityComparableTo(source: Type, target: Type) { + const sourceIsLiteral = isLiteralType(source); + const targetIsLiteral = isLiteralType(target); + if (!sourceIsLiteral || !targetIsLiteral) { + source = sourceIsLiteral ? getBaseTypeOfLiteralType(source) : source; + target = targetIsLiteral ? getBaseTypeOfLiteralType(target) : target; + } return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } @@ -13672,12 +13678,6 @@ namespace ts { case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: - const leftIsLiteral = isLiteralType(leftType); - const rightIsLiteral = isLiteralType(rightType); - if (!leftIsLiteral || !rightIsLiteral) { - leftType = leftIsLiteral ? getBaseTypeOfLiteralType(leftType) : leftType; - rightType = rightIsLiteral ? getBaseTypeOfLiteralType(rightType) : rightType; - } if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) { reportOperatorError(); } @@ -16584,7 +16584,7 @@ namespace ts { let firstDefaultClause: CaseOrDefaultClause; let hasDuplicateDefaultClause = false; - let expressionType = checkExpression(node.expression); + const expressionType = checkExpression(node.expression); 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) { @@ -16605,13 +16605,7 @@ 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. - let caseType = checkExpression(caseClause.expression); - const expressionIsLiteral = isLiteralType(expressionType); - const caseIsLiteral = isLiteralType(caseType); - if (!expressionIsLiteral || !caseIsLiteral) { - expressionType = expressionIsLiteral ? getBaseTypeOfLiteralType(expressionType) : expressionType; - caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; - } + const caseType = checkExpression(caseClause.expression); if (!isTypeEqualityComparableTo(expressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined); From 83d6abcd9eb829c9ac01376d27741228e09d1ae6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 14 Oct 2016 08:59:00 -0700 Subject: [PATCH 4/9] Update baselines There are more literal types in error messages because error reporting no longer has the advantage of calls to getBaseLiteralType --- tests/baselines/reference/expr.errors.txt | 8 ++++---- .../for-inStatementsArrayErrors.errors.txt | 4 ++-- ...AssertionsInEqualityComparisons02.errors.txt | 4 ++-- .../reference/switchAssignmentCompat.errors.txt | 4 ++-- .../switchCaseCircularRefeference.errors.txt | 10 ++++++---- ...switchCasesExpressionTypeMismatch.errors.txt | 17 ++++++++++------- .../baselines/reference/symbolType9.errors.txt | 16 ++++++++-------- 7 files changed, 34 insertions(+), 29 deletions(-) diff --git a/tests/baselines/reference/expr.errors.txt b/tests/baselines/reference/expr.errors.txt index 23215cac6bae2..52f6f64fef287 100644 --- a/tests/baselines/reference/expr.errors.txt +++ b/tests/baselines/reference/expr.errors.txt @@ -1,7 +1,7 @@ tests/cases/compiler/expr.ts(87,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'string'. -tests/cases/compiler/expr.ts(88,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'boolean'. +tests/cases/compiler/expr.ts(88,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'false'. tests/cases/compiler/expr.ts(94,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. -tests/cases/compiler/expr.ts(95,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'boolean'. +tests/cases/compiler/expr.ts(95,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'false'. tests/cases/compiler/expr.ts(98,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'E'. tests/cases/compiler/expr.ts(115,5): error TS2365: Operator '==' cannot be applied to types 'E' and 'string'. tests/cases/compiler/expr.ts(116,5): error TS2365: Operator '==' cannot be applied to types 'E' and 'false'. @@ -161,7 +161,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari !!! error TS2365: Operator '==' cannot be applied to types 'number' and 'string'. n==b; ~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'number' and 'boolean'. +!!! error TS2365: Operator '==' cannot be applied to types 'number' and 'false'. n==i; n==n; n==e; @@ -172,7 +172,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari !!! error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. s==b; ~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'boolean'. +!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'false'. s==i; s==s; s==e; diff --git a/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt b/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt index 6886d992bb2e5..e639d8452de21 100644 --- a/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt +++ b/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(5,14): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(6,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(7,9): error TS2365: Operator '===' cannot be applied to types 'string' and 'number'. +tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(7,9): error TS2365: Operator '===' cannot be applied to types 'string' and '1'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(9,16): error TS2339: Property 'unknownProperty' does not exist on type 'string'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(13,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'i' must be of type 'number', but here has type 'string'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(17,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'j' must be of type 'any', but here has type 'string'. @@ -19,7 +19,7 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors. !!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. if (x === 1) { ~~~~~~~ -!!! error TS2365: Operator '===' cannot be applied to types 'string' and 'number'. +!!! error TS2365: Operator '===' cannot be applied to types 'string' and '1'. } let a3 = x.unknownProperty; ~~~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt b/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt index fd92af4e84f63..81adf58dbde90 100644 --- a/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt +++ b/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(3,9): error TS2365: Operator '===' cannot be applied to types '"foo"' and '"baz"'. -tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. +tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2365: Operator '==' cannot be applied to types '"foo"' and 'number'. tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,19): error TS2352: Type 'string' cannot be converted to type 'number'. @@ -12,7 +12,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparis var b = "foo" !== ("bar" as "foo"); var c = "foo" == ("bar"); ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. +!!! error TS2365: Operator '==' cannot be applied to types '"foo"' and 'number'. ~~~~~~~~~~~~~ !!! error TS2352: Type 'string' cannot be converted to type 'number'. var d = "foo" === ("bar" as EnhancedString); \ No newline at end of file diff --git a/tests/baselines/reference/switchAssignmentCompat.errors.txt b/tests/baselines/reference/switchAssignmentCompat.errors.txt index 747a1035b6846..46c89fa5b52d2 100644 --- a/tests/baselines/reference/switchAssignmentCompat.errors.txt +++ b/tests/baselines/reference/switchAssignmentCompat.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type 'number'. +tests/cases/compiler/switchAssignmentCompat.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'. ==== 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 'number'. +!!! error TS2678: Type 'typeof Foo' is not comparable to type '0'. } \ No newline at end of file diff --git a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt index bc576f8e723b8..9ee571de468ce 100644 --- a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt +++ b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt @@ -1,5 +1,6 @@ -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(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 (1 errors) ==== @@ -9,8 +10,9 @@ 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 'string'. -!!! error TS2678: Type '{ a: "C"; e: any; }' is not comparable to type 'string'. +!!! 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"'. break; } } \ No newline at end of file diff --git a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt index 26d39efef48ec..3ea12e2460f65 100644 --- a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt +++ b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt @@ -1,22 +1,25 @@ -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 'string' is not comparable to type 'number'. -tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'boolean' is not comparable to type 'number'. +tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'. +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'. -==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (3 errors) ==== +==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (4 errors) ==== class Foo { } switch (0) { case Foo: break; // Error ~~~ -!!! error TS2678: Type 'typeof Foo' is not comparable to type 'number'. +!!! error TS2678: Type 'typeof Foo' is not comparable to type '0'. case "sss": break; // Error ~~~~~ -!!! error TS2678: Type 'string' is not comparable to type 'number'. +!!! error TS2678: Type '"sss"' is not comparable to type '0'. case 123: break; // No Error + ~~~ +!!! error TS2678: Type '123' is not comparable to type '0'. case true: break; // Error ~~~~ -!!! error TS2678: Type 'boolean' is not comparable to type 'number'. +!!! error TS2678: Type 'true' is not comparable to type '0'. } var s: any = 0; diff --git a/tests/baselines/reference/symbolType9.errors.txt b/tests/baselines/reference/symbolType9.errors.txt index 5506fdc6f9dac..55a766a379e80 100644 --- a/tests/baselines/reference/symbolType9.errors.txt +++ b/tests/baselines/reference/symbolType9.errors.txt @@ -1,7 +1,7 @@ -tests/cases/conformance/es6/Symbols/symbolType9.ts(3,1): error TS2365: Operator '==' cannot be applied to types 'symbol' and 'boolean'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2365: Operator '!=' cannot be applied to types 'number' and 'symbol'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2365: Operator '===' cannot be applied to types 'symbol' and 'number'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator '!==' cannot be applied to types 'boolean' and 'symbol'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(3,1): error TS2365: Operator '==' cannot be applied to types 'symbol' and 'true'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2365: Operator '!=' cannot be applied to types '0' and 'symbol'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2365: Operator '===' cannot be applied to types 'symbol' and '1'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator '!==' cannot be applied to types 'false' and 'symbol'. ==== tests/cases/conformance/es6/Symbols/symbolType9.ts (4 errors) ==== @@ -9,16 +9,16 @@ tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator s == s; s == true; ~~~~~~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'symbol' and 'boolean'. +!!! error TS2365: Operator '==' cannot be applied to types 'symbol' and 'true'. s != s; 0 != s; ~~~~~~ -!!! error TS2365: Operator '!=' cannot be applied to types 'number' and 'symbol'. +!!! error TS2365: Operator '!=' cannot be applied to types '0' and 'symbol'. s === s; s === 1; ~~~~~~~ -!!! error TS2365: Operator '===' cannot be applied to types 'symbol' and 'number'. +!!! error TS2365: Operator '===' cannot be applied to types 'symbol' and '1'. s !== s; false !== s; ~~~~~~~~~~~ -!!! error TS2365: Operator '!==' cannot be applied to types 'boolean' and 'symbol'. \ No newline at end of file +!!! error TS2365: Operator '!==' cannot be applied to types 'false' and 'symbol'. \ No newline at end of file From ebef408dfa18061fb3ae984bbc2f0315177d756b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 31 Oct 2016 14:44:04 -0700 Subject: [PATCH 5/9] Move eitherIsNotLiteral check into switch and === checks This improves error messages --- src/compiler/checker.ts | 22 ++++++++++++------- tests/baselines/reference/expr.errors.txt | 8 +++---- .../for-inStatementsArrayErrors.errors.txt | 4 ++-- ...sertionsInEqualityComparisons02.errors.txt | 4 ++-- .../switchAssignmentCompat.errors.txt | 4 ++-- .../switchCaseCircularRefeference.errors.txt | 10 ++++----- ...itchCasesExpressionTypeMismatch.errors.txt | 17 ++++++-------- .../reference/symbolType9.errors.txt | 16 +++++++------- 8 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d76d4e1e77018..2d05e16ef825e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13814,12 +13814,6 @@ namespace ts { } function isTypeEqualityComparableTo(source: Type, target: Type) { - const sourceIsLiteral = isLiteralType(source); - const targetIsLiteral = isLiteralType(target); - if (!sourceIsLiteral || !targetIsLiteral) { - source = sourceIsLiteral ? getBaseTypeOfLiteralType(source) : source; - target = targetIsLiteral ? getBaseTypeOfLiteralType(target) : target; - } return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } @@ -13960,6 +13954,12 @@ namespace ts { case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: + const leftIsLiteral = isLiteralType(leftType); + const rightIsLiteral = isLiteralType(rightType); + if (!leftIsLiteral || !rightIsLiteral) { + leftType = leftIsLiteral ? getBaseTypeOfLiteralType(leftType) : leftType; + rightType = rightIsLiteral ? getBaseTypeOfLiteralType(rightType) : rightType; + } if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) { reportOperatorError(); } @@ -16882,7 +16882,7 @@ namespace ts { let firstDefaultClause: CaseOrDefaultClause; let hasDuplicateDefaultClause = false; - const expressionType = checkExpression(node.expression); + let expressionType = checkExpression(node.expression); 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) { @@ -16903,7 +16903,13 @@ 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); + let caseType = checkExpression(caseClause.expression); + const caseIsLiteral = isLiteralType(caseType); + const expressionIsLiteral = isLiteralType(expressionType); + if (!caseIsLiteral || !expressionIsLiteral) { + caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; + expressionType = expressionIsLiteral ? getBaseTypeOfLiteralType(expressionType) : expressionType; + } if (!isTypeEqualityComparableTo(expressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined); diff --git a/tests/baselines/reference/expr.errors.txt b/tests/baselines/reference/expr.errors.txt index 52f6f64fef287..23215cac6bae2 100644 --- a/tests/baselines/reference/expr.errors.txt +++ b/tests/baselines/reference/expr.errors.txt @@ -1,7 +1,7 @@ tests/cases/compiler/expr.ts(87,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'string'. -tests/cases/compiler/expr.ts(88,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'false'. +tests/cases/compiler/expr.ts(88,5): error TS2365: Operator '==' cannot be applied to types 'number' and 'boolean'. tests/cases/compiler/expr.ts(94,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. -tests/cases/compiler/expr.ts(95,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'false'. +tests/cases/compiler/expr.ts(95,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'boolean'. tests/cases/compiler/expr.ts(98,5): error TS2365: Operator '==' cannot be applied to types 'string' and 'E'. tests/cases/compiler/expr.ts(115,5): error TS2365: Operator '==' cannot be applied to types 'E' and 'string'. tests/cases/compiler/expr.ts(116,5): error TS2365: Operator '==' cannot be applied to types 'E' and 'false'. @@ -161,7 +161,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari !!! error TS2365: Operator '==' cannot be applied to types 'number' and 'string'. n==b; ~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'number' and 'false'. +!!! error TS2365: Operator '==' cannot be applied to types 'number' and 'boolean'. n==i; n==n; n==e; @@ -172,7 +172,7 @@ tests/cases/compiler/expr.ts(242,7): error TS2363: The right-hand side of an ari !!! error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. s==b; ~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'false'. +!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'boolean'. s==i; s==s; s==e; diff --git a/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt b/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt index e639d8452de21..6886d992bb2e5 100644 --- a/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt +++ b/tests/baselines/reference/for-inStatementsArrayErrors.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(5,14): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(6,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(7,9): error TS2365: Operator '===' cannot be applied to types 'string' and '1'. +tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(7,9): error TS2365: Operator '===' cannot be applied to types 'string' and 'number'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(9,16): error TS2339: Property 'unknownProperty' does not exist on type 'string'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(13,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'i' must be of type 'number', but here has type 'string'. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(17,10): error TS2403: Subsequent variable declarations must have the same type. Variable 'j' must be of type 'any', but here has type 'string'. @@ -19,7 +19,7 @@ tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors. !!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. if (x === 1) { ~~~~~~~ -!!! error TS2365: Operator '===' cannot be applied to types 'string' and '1'. +!!! error TS2365: Operator '===' cannot be applied to types 'string' and 'number'. } let a3 = x.unknownProperty; ~~~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt b/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt index 81adf58dbde90..fd92af4e84f63 100644 --- a/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt +++ b/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(3,9): error TS2365: Operator '===' cannot be applied to types '"foo"' and '"baz"'. -tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2365: Operator '==' cannot be applied to types '"foo"' and 'number'. +tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,9): error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparisons02.ts(5,19): error TS2352: Type 'string' cannot be converted to type 'number'. @@ -12,7 +12,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparis var b = "foo" !== ("bar" as "foo"); var c = "foo" == ("bar"); ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2365: Operator '==' cannot be applied to types '"foo"' and 'number'. +!!! error TS2365: Operator '==' cannot be applied to types 'string' and 'number'. ~~~~~~~~~~~~~ !!! error TS2352: Type 'string' cannot be converted to type 'number'. var d = "foo" === ("bar" as EnhancedString); \ No newline at end of file diff --git a/tests/baselines/reference/switchAssignmentCompat.errors.txt b/tests/baselines/reference/switchAssignmentCompat.errors.txt index 46c89fa5b52d2..747a1035b6846 100644 --- a/tests/baselines/reference/switchAssignmentCompat.errors.txt +++ b/tests/baselines/reference/switchAssignmentCompat.errors.txt @@ -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'. } \ No newline at end of file diff --git a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt index 9ee571de468ce..bc576f8e723b8 100644 --- a/tests/baselines/reference/switchCaseCircularRefeference.errors.txt +++ b/tests/baselines/reference/switchCaseCircularRefeference.errors.txt @@ -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; } } \ No newline at end of file diff --git a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt index 3ea12e2460f65..26d39efef48ec 100644 --- a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt +++ b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt @@ -1,25 +1,22 @@ -tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(4,10): error TS2678: Type 'typeof Foo' is not comparable to type '0'. -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'. +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 'string' is not comparable to type 'number'. +tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'boolean' is not comparable to type 'number'. -==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (4 errors) ==== +==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (3 errors) ==== class Foo { } 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'. +!!! error TS2678: Type 'string' is not comparable to type 'number'. case 123: break; // No Error - ~~~ -!!! error TS2678: Type '123' is not comparable to type '0'. case true: break; // Error ~~~~ -!!! error TS2678: Type 'true' is not comparable to type '0'. +!!! error TS2678: Type 'boolean' is not comparable to type 'number'. } var s: any = 0; diff --git a/tests/baselines/reference/symbolType9.errors.txt b/tests/baselines/reference/symbolType9.errors.txt index 55a766a379e80..5506fdc6f9dac 100644 --- a/tests/baselines/reference/symbolType9.errors.txt +++ b/tests/baselines/reference/symbolType9.errors.txt @@ -1,7 +1,7 @@ -tests/cases/conformance/es6/Symbols/symbolType9.ts(3,1): error TS2365: Operator '==' cannot be applied to types 'symbol' and 'true'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2365: Operator '!=' cannot be applied to types '0' and 'symbol'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2365: Operator '===' cannot be applied to types 'symbol' and '1'. -tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator '!==' cannot be applied to types 'false' and 'symbol'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(3,1): error TS2365: Operator '==' cannot be applied to types 'symbol' and 'boolean'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2365: Operator '!=' cannot be applied to types 'number' and 'symbol'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2365: Operator '===' cannot be applied to types 'symbol' and 'number'. +tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator '!==' cannot be applied to types 'boolean' and 'symbol'. ==== tests/cases/conformance/es6/Symbols/symbolType9.ts (4 errors) ==== @@ -9,16 +9,16 @@ tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2365: Operator s == s; s == true; ~~~~~~~~~ -!!! error TS2365: Operator '==' cannot be applied to types 'symbol' and 'true'. +!!! error TS2365: Operator '==' cannot be applied to types 'symbol' and 'boolean'. s != s; 0 != s; ~~~~~~ -!!! error TS2365: Operator '!=' cannot be applied to types '0' and 'symbol'. +!!! error TS2365: Operator '!=' cannot be applied to types 'number' and 'symbol'. s === s; s === 1; ~~~~~~~ -!!! error TS2365: Operator '===' cannot be applied to types 'symbol' and '1'. +!!! error TS2365: Operator '===' cannot be applied to types 'symbol' and 'number'. s !== s; false !== s; ~~~~~~~~~~~ -!!! error TS2365: Operator '!==' cannot be applied to types 'false' and 'symbol'. \ No newline at end of file +!!! error TS2365: Operator '!==' cannot be applied to types 'boolean' and 'symbol'. \ No newline at end of file From d7f5fc8fcf1c083029588217303324a145c20650 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 1 Nov 2016 09:09:40 -0700 Subject: [PATCH 6/9] Get literal type only once --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2d05e16ef825e..326d84278be24 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16882,7 +16882,8 @@ namespace ts { let firstDefaultClause: CaseOrDefaultClause; let hasDuplicateDefaultClause = false; - let expressionType = checkExpression(node.expression); + 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) { @@ -16905,14 +16906,14 @@ namespace ts { // to or from the type of the 'switch' expression. let caseType = checkExpression(caseClause.expression); const caseIsLiteral = isLiteralType(caseType); - const expressionIsLiteral = isLiteralType(expressionType); + let literalExpressionType = expressionType; if (!caseIsLiteral || !expressionIsLiteral) { caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; - expressionType = expressionIsLiteral ? getBaseTypeOfLiteralType(expressionType) : expressionType; + literalExpressionType = getBaseTypeOfLiteralType(expressionType); } - if (!isTypeEqualityComparableTo(expressionType, caseType)) { + if (!isTypeEqualityComparableTo(literalExpressionType, 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, literalExpressionType, caseClause.expression, /*headMessage*/ undefined); } } forEach(clause.statements, checkSourceElement); From 357d58b4f1ea650e8b261a45aef66febaaadc55e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 1 Nov 2016 09:10:39 -0700 Subject: [PATCH 7/9] Update baselines --- ...witchCasesExpressionTypeMismatch.errors.txt | 18 +++++++++++------- .../switchCasesExpressionTypeMismatch.js | 7 ++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt index 26d39efef48ec..7a5fd12182e4e 100644 --- a/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt +++ b/tests/baselines/reference/switchCasesExpressionTypeMismatch.errors.txt @@ -1,9 +1,10 @@ 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 'string' is not comparable to type 'number'. -tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: Type 'boolean' 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'. -==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (3 errors) ==== +==== tests/cases/compiler/switchCasesExpressionTypeMismatch.ts (4 errors) ==== class Foo { } switch (0) { @@ -12,11 +13,13 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T !!! error TS2678: Type 'typeof Foo' is not comparable to type 'number'. case "sss": break; // Error ~~~~~ -!!! error TS2678: Type 'string' is not comparable to type 'number'. - case 123: break; // No Error +!!! error TS2678: Type '"sss"' is not comparable to type '0'. + case 123: break; // Error + ~~~ +!!! error TS2678: Type '123' is not comparable to type '0'. case true: break; // Error ~~~~ -!!! error TS2678: Type 'boolean' is not comparable to type 'number'. +!!! error TS2678: Type 'true' is not comparable to type '0'. } var s: any = 0; @@ -27,4 +30,5 @@ tests/cases/compiler/switchCasesExpressionTypeMismatch.ts(7,10): error TS2678: T case "sss": break; case 123: break; case true: break; - } \ No newline at end of file + } + \ No newline at end of file diff --git a/tests/baselines/reference/switchCasesExpressionTypeMismatch.js b/tests/baselines/reference/switchCasesExpressionTypeMismatch.js index ba929691d2536..43a4322457938 100644 --- a/tests/baselines/reference/switchCasesExpressionTypeMismatch.js +++ b/tests/baselines/reference/switchCasesExpressionTypeMismatch.js @@ -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; From d58a13f6a1c8020e9ab8d88355300e1eb81b5134 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 1 Nov 2016 10:07:30 -0700 Subject: [PATCH 8/9] Add missed test update --- tests/cases/compiler/switchCasesExpressionTypeMismatch.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/compiler/switchCasesExpressionTypeMismatch.ts b/tests/cases/compiler/switchCasesExpressionTypeMismatch.ts index 4de01ebba175c..ee547705b84d2 100644 --- a/tests/cases/compiler/switchCasesExpressionTypeMismatch.ts +++ b/tests/cases/compiler/switchCasesExpressionTypeMismatch.ts @@ -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; -} \ No newline at end of file +} From 1866d0580c4801e3d4284e5952026de680643945 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 7 Nov 2016 11:16:54 -0800 Subject: [PATCH 9/9] Rename variable in checkSwitchStatement per PR --- src/compiler/checker.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 326d84278be24..38d3a97a2874d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16906,14 +16906,14 @@ namespace ts { // to or from the type of the 'switch' expression. let caseType = checkExpression(caseClause.expression); const caseIsLiteral = isLiteralType(caseType); - let literalExpressionType = expressionType; + let comparedExpressionType = expressionType; if (!caseIsLiteral || !expressionIsLiteral) { caseType = caseIsLiteral ? getBaseTypeOfLiteralType(caseType) : caseType; - literalExpressionType = getBaseTypeOfLiteralType(expressionType); + comparedExpressionType = getBaseTypeOfLiteralType(expressionType); } - if (!isTypeEqualityComparableTo(literalExpressionType, caseType)) { + if (!isTypeEqualityComparableTo(comparedExpressionType, caseType)) { // expressionType is not comparable to caseType, try the reversed check and report errors if it fails - checkTypeComparableTo(caseType, literalExpressionType, caseClause.expression, /*headMessage*/ undefined); + checkTypeComparableTo(caseType, comparedExpressionType, caseClause.expression, /*headMessage*/ undefined); } } forEach(clause.statements, checkSourceElement);