From 6487d1ffe0d903b1e55a187ddeb973fe0d445a2f Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Tue, 23 Apr 2019 15:59:07 -0400 Subject: [PATCH] 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. (#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 #26758. --- src/compiler/checker.ts | 6 ------ ...terConstrainedByLiteralToLiteral.errors.txt | 13 +++++++++++++ ...peParameterConstrainedByLiteralToLiteral.js | 15 +++++++++++++++ ...ameterConstrainedByLiteralToLiteral.symbols | 16 ++++++++++++++++ ...arameterConstrainedByLiteralToLiteral.types | 18 ++++++++++++++++++ tests/baselines/reference/expr.errors.txt | 8 ++++---- .../for-inStatementsArrayErrors.errors.txt | 4 ++-- ...ssertionsInEqualityComparisons02.errors.txt | 4 ++-- ...ringLiteralsWithEqualityChecks03.errors.txt | 14 +++++++++++++- ...ringLiteralsWithEqualityChecks04.errors.txt | 14 +++++++++++++- .../baselines/reference/symbolType9.errors.txt | 16 ++++++++-------- ...peParameterConstrainedByLiteralToLiteral.ts | 6 ++++++ 12 files changed, 110 insertions(+), 24 deletions(-) create mode 100644 tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt create mode 100644 tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.js create mode 100644 tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.symbols create mode 100644 tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types create mode 100644 tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d1f8d0790e8..f1b4f06abbc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23320,12 +23320,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(); } diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt new file mode 100644 index 00000000000..aa8a0572e6b --- /dev/null +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts(5,5): error TS2367: This condition will always return 'false' since the types 'T' and '"x"' have no overlap. + + +==== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts (1 errors) ==== + // Test for #26758 + + function foo(t: T) { + t === "a"; // Should be allowed + t === "x"; // Should be error + ~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types 'T' and '"x"' have no overlap. + } + \ No newline at end of file diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.js b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.js new file mode 100644 index 00000000000..6d7824cfd90 --- /dev/null +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.js @@ -0,0 +1,15 @@ +//// [compareTypeParameterConstrainedByLiteralToLiteral.ts] +// Test for #26758 + +function foo(t: T) { + t === "a"; // Should be allowed + t === "x"; // Should be error +} + + +//// [compareTypeParameterConstrainedByLiteralToLiteral.js] +// Test for #26758 +function foo(t) { + t === "a"; // Should be allowed + t === "x"; // Should be error +} diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.symbols b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.symbols new file mode 100644 index 00000000000..dd8c5916063 --- /dev/null +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts === +// Test for #26758 + +function foo(t: T) { +>foo : Symbol(foo, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 0, 0)) +>T : Symbol(T, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 13)) +>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34)) +>T : Symbol(T, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 13)) + + t === "a"; // Should be allowed +>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34)) + + t === "x"; // Should be error +>t : Symbol(t, Decl(compareTypeParameterConstrainedByLiteralToLiteral.ts, 2, 34)) +} + diff --git a/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types new file mode 100644 index 00000000000..96792a8b9f0 --- /dev/null +++ b/tests/baselines/reference/compareTypeParameterConstrainedByLiteralToLiteral.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts === +// Test for #26758 + +function foo(t: T) { +>foo : (t: T) => void +>t : T + + t === "a"; // Should be allowed +>t === "a" : boolean +>t : T +>"a" : "a" + + t === "x"; // Should be error +>t === "x" : boolean +>t : T +>"x" : "x" +} + diff --git a/tests/baselines/reference/expr.errors.txt b/tests/baselines/reference/expr.errors.txt index 5acfd691d58..e1d6a9e4d45 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 TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap. -tests/cases/compiler/expr.ts(88,5): error TS2367: This condition will always return 'false' since the types 'number' and 'boolean' have no overlap. +tests/cases/compiler/expr.ts(88,5): error TS2367: This condition will always return 'false' since the types 'number' and 'false' have no overlap. tests/cases/compiler/expr.ts(94,5): error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap. -tests/cases/compiler/expr.ts(95,5): error TS2367: This condition will always return 'false' since the types 'string' and 'boolean' have no overlap. +tests/cases/compiler/expr.ts(95,5): error TS2367: This condition will always return 'false' since the types 'string' and 'false' have no overlap. tests/cases/compiler/expr.ts(98,5): error TS2367: This condition will always return 'false' since the types 'string' and 'E' have no overlap. tests/cases/compiler/expr.ts(115,5): error TS2367: This condition will always return 'false' since the types 'E' and 'string' have no overlap. 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 !!! error TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap. n==b; ~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'number' and 'boolean' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types 'number' and 'false' have no overlap. 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 TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap. s==b; ~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'string' and 'boolean' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types 'string' and 'false' have no overlap. 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 568412b563b..3ba7a73367f 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(4,16): 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(5,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -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. +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. tests/cases/conformance/statements/for-inStatements/for-inStatementsArrayErrors.ts(8,16): error TS2339: Property 'unknownProperty' does not exist on type 'string'. 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'. 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. !!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. if (x === 1) { ~~~~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types 'string' and '1' have no overlap. } let a3 = x.unknownProperty; ~~~~~~~~~~~~~~~ diff --git a/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt b/tests/baselines/reference/stringLiteralsAssertionsInEqualityComparisons02.errors.txt index 8cb9e8203e7..b98939167aa 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 TS2367: This condition will always return 'false' since the types '"foo"' and '"baz"' have no overlap. -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. +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. 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. @@ -12,7 +12,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssertionsInEqualityComparis var b = "foo" !== ("bar" as "foo"); var c = "foo" == ("bar"); ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types '"foo"' and 'number' have no overlap. ~~~~~~~~~~~~~ !!! 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. var d = "foo" === ("bar" as EnhancedString); \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralsWithEqualityChecks03.errors.txt b/tests/baselines/reference/stringLiteralsWithEqualityChecks03.errors.txt index cb252516450..cbefbbac978 100644 --- a/tests/baselines/reference/stringLiteralsWithEqualityChecks03.errors.txt +++ b/tests/baselines/reference/stringLiteralsWithEqualityChecks03.errors.txt @@ -1,8 +1,12 @@ 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. +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. +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. 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. +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. +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. -==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts (2 errors) ==== +==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts (6 errors) ==== interface Runnable { isRunning: boolean; } @@ -24,7 +28,11 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(25,5 b = "bar" === x; b = x === "bar"; b = y === "bar"; + ~~~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap. b = "bar" === y; + ~~~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap. b = x !== y; b = "foo" !== y @@ -35,5 +43,9 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks03.ts(25,5 b = "bar" !== x; b = x !== "bar"; b = y !== "bar"; + ~~~~~~~~~~~ +!!! error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap. b = "bar" !== y; + ~~~~~~~~~~~ +!!! error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap. \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralsWithEqualityChecks04.errors.txt b/tests/baselines/reference/stringLiteralsWithEqualityChecks04.errors.txt index 8bdee5e1617..293abe0b5c5 100644 --- a/tests/baselines/reference/stringLiteralsWithEqualityChecks04.errors.txt +++ b/tests/baselines/reference/stringLiteralsWithEqualityChecks04.errors.txt @@ -1,8 +1,12 @@ 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. +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. +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. 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. +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. +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. -==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts (2 errors) ==== +==== tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts (6 errors) ==== interface Runnable { isRunning: boolean; } @@ -24,7 +28,11 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(25,5 b = "bar" == x; b = x == "bar"; b = y == "bar"; + ~~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap. b = "bar" == y; + ~~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap. b = x != y; b = "foo" != y @@ -35,5 +43,9 @@ tests/cases/conformance/types/literal/stringLiteralsWithEqualityChecks04.ts(25,5 b = "bar" != x; b = x != "bar"; b = y != "bar"; + ~~~~~~~~~~ +!!! error TS2367: This condition will always return 'true' since the types 'Refrigerator | "foo"' and '"bar"' have no overlap. b = "bar" != y; + ~~~~~~~~~~ +!!! error TS2367: This condition will always return 'true' since the types '"bar"' and 'Refrigerator | "foo"' have no overlap. \ No newline at end of file diff --git a/tests/baselines/reference/symbolType9.errors.txt b/tests/baselines/reference/symbolType9.errors.txt index 5b6c233367e..e697a261929 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 TS2367: This condition will always return 'false' since the types 'symbol' and 'boolean' have no overlap. -tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2367: This condition will always return 'true' since the types 'number' and 'symbol' have no overlap. -tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2367: This condition will always return 'false' since the types 'symbol' and 'number' have no overlap. -tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2367: This condition will always return 'true' since the types 'boolean' and 'symbol' have no overlap. +tests/cases/conformance/es6/Symbols/symbolType9.ts(3,1): error TS2367: This condition will always return 'false' since the types 'symbol' and 'true' have no overlap. +tests/cases/conformance/es6/Symbols/symbolType9.ts(5,1): error TS2367: This condition will always return 'true' since the types '0' and 'symbol' have no overlap. +tests/cases/conformance/es6/Symbols/symbolType9.ts(7,1): error TS2367: This condition will always return 'false' since the types 'symbol' and '1' have no overlap. +tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2367: This condition will always return 'true' since the types 'false' and 'symbol' have no overlap. ==== tests/cases/conformance/es6/Symbols/symbolType9.ts (4 errors) ==== @@ -9,16 +9,16 @@ tests/cases/conformance/es6/Symbols/symbolType9.ts(9,1): error TS2367: This cond s == s; s == true; ~~~~~~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'symbol' and 'boolean' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types 'symbol' and 'true' have no overlap. s != s; 0 != s; ~~~~~~ -!!! error TS2367: This condition will always return 'true' since the types 'number' and 'symbol' have no overlap. +!!! error TS2367: This condition will always return 'true' since the types '0' and 'symbol' have no overlap. s === s; s === 1; ~~~~~~~ -!!! error TS2367: This condition will always return 'false' since the types 'symbol' and 'number' have no overlap. +!!! error TS2367: This condition will always return 'false' since the types 'symbol' and '1' have no overlap. s !== s; false !== s; ~~~~~~~~~~~ -!!! error TS2367: This condition will always return 'true' since the types 'boolean' and 'symbol' have no overlap. \ No newline at end of file +!!! error TS2367: This condition will always return 'true' since the types 'false' and 'symbol' have no overlap. \ No newline at end of file diff --git a/tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts b/tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts new file mode 100644 index 00000000000..eb92c64c3bf --- /dev/null +++ b/tests/cases/compiler/compareTypeParameterConstrainedByLiteralToLiteral.ts @@ -0,0 +1,6 @@ +// Test for #26758 + +function foo(t: T) { + t === "a"; // Should be allowed + t === "x"; // Should be error +}