Skip to content

Commit

Permalink
feat: support error when comparing with object/array literals (#45978)
Browse files Browse the repository at this point in the history
* feat: support error when comparing with object/array literals

* chore: include regexp, function and class literal

* chore: include regexp, function and class literal

* test: update baseline

* fix: baseline
  • Loading branch information
Jack-Works authored May 12, 2022
1 parent da00ba6 commit b689cd0
Show file tree
Hide file tree
Showing 12 changed files with 768 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33977,6 +33977,10 @@ namespace ts {
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) {
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
}
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
return booleanType;

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3459,6 +3459,10 @@
"category": "Error",
"code": 2838
},
"This condition will always return '{0}' since JavaScript compares objects by reference, not value.": {
"category": "Error",
"code": 2839
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,19 @@ namespace ts {
return isLiteralKind(node.kind);
}

/** @internal */
export function isLiteralExpressionOfObject(node: Node) {
switch (node.kind) {
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
return true;
}
return false;
}

// Pseudo-literals

/* @internal */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(4,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(6,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(8,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(10,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(12,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(14,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(17,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(19,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(21,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(23,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(25,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(27,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(30,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(32,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(34,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(36,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(38,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(40,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(43,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(45,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(47,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(49,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(51,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(53,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.


==== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts (24 errors) ====
const a = { a: 1 }
const b = [1]

if ({ a: 1 } === { a: 1 }) {
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ([1] === [1]) {
~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if (a === { a: 1 }) {
~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if (b === [1]) {
~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ({ a: 1 } === a) {
~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ([1] === b) {
~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}

if ({ a: 1 } !== { a: 1 }) {
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ([1] !== [1]) {
~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if (a !== { a: 1 }) {
~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if (b !== [1]) {
~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ({ a: 1 } !== a) {
~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ([1] !== b) {
~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}

if ({ a: 1 } == { a: 1 }) {
~~~~~~~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ([1] == [1]) {
~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if (a == { a: 1 }) {
~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if (b == [1]) {
~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ({ a: 1 } == a) {
~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}
if ([1] == b) {
~~~~~~~~
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}

if ({ a: 1 } != { a: 1 }) {
~~~~~~~~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ([1] != [1]) {
~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if (a != { a: 1 }) {
~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if (b != [1]) {
~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ({ a: 1 } != a) {
~~~~~~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}
if ([1] != b) {
~~~~~~~~
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
}

108 changes: 108 additions & 0 deletions tests/baselines/reference/conditionalEqualityOnLiteralObjects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//// [conditionalEqualityOnLiteralObjects.ts]
const a = { a: 1 }
const b = [1]

if ({ a: 1 } === { a: 1 }) {
}
if ([1] === [1]) {
}
if (a === { a: 1 }) {
}
if (b === [1]) {
}
if ({ a: 1 } === a) {
}
if ([1] === b) {
}

if ({ a: 1 } !== { a: 1 }) {
}
if ([1] !== [1]) {
}
if (a !== { a: 1 }) {
}
if (b !== [1]) {
}
if ({ a: 1 } !== a) {
}
if ([1] !== b) {
}

if ({ a: 1 } == { a: 1 }) {
}
if ([1] == [1]) {
}
if (a == { a: 1 }) {
}
if (b == [1]) {
}
if ({ a: 1 } == a) {
}
if ([1] == b) {
}

if ({ a: 1 } != { a: 1 }) {
}
if ([1] != [1]) {
}
if (a != { a: 1 }) {
}
if (b != [1]) {
}
if ({ a: 1 } != a) {
}
if ([1] != b) {
}


//// [conditionalEqualityOnLiteralObjects.js]
var a = { a: 1 };
var b = [1];
if ({ a: 1 } === { a: 1 }) {
}
if ([1] === [1]) {
}
if (a === { a: 1 }) {
}
if (b === [1]) {
}
if ({ a: 1 } === a) {
}
if ([1] === b) {
}
if ({ a: 1 } !== { a: 1 }) {
}
if ([1] !== [1]) {
}
if (a !== { a: 1 }) {
}
if (b !== [1]) {
}
if ({ a: 1 } !== a) {
}
if ([1] !== b) {
}
if ({ a: 1 } == { a: 1 }) {
}
if ([1] == [1]) {
}
if (a == { a: 1 }) {
}
if (b == [1]) {
}
if ({ a: 1 } == a) {
}
if ([1] == b) {
}
if ({ a: 1 } != { a: 1 }) {
}
if ([1] != [1]) {
}
if (a != { a: 1 }) {
}
if (b != [1]) {
}
if ({ a: 1 } != a) {
}
if ([1] != b) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
=== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts ===
const a = { a: 1 }
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 11))

const b = [1]
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))

if ({ a: 1 } === { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 18))
}
if ([1] === [1]) {
}
if (a === { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 7, 11))
}
if (b === [1]) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}
if ({ a: 1 } === a) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 11, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
}
if ([1] === b) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}

if ({ a: 1 } !== { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 18))
}
if ([1] !== [1]) {
}
if (a !== { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 20, 11))
}
if (b !== [1]) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}
if ({ a: 1 } !== a) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 24, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
}
if ([1] !== b) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}

if ({ a: 1 } == { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 17))
}
if ([1] == [1]) {
}
if (a == { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 33, 10))
}
if (b == [1]) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}
if ({ a: 1 } == a) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 37, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
}
if ([1] == b) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}

if ({ a: 1 } != { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 17))
}
if ([1] != [1]) {
}
if (a != { a: 1 }) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 46, 10))
}
if (b != [1]) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}
if ({ a: 1 } != a) {
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 50, 5))
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
}
if ([1] != b) {
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
}

Loading

0 comments on commit b689cd0

Please sign in to comment.