Skip to content

Commit b689cd0

Browse files
authored
feat: support error when comparing with object/array literals (#45978)
* 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
1 parent da00ba6 commit b689cd0

12 files changed

+768
-3
lines changed

src/compiler/checker.ts

+4
Original file line numberDiff line numberDiff line change
@@ -33977,6 +33977,10 @@ namespace ts {
3397733977
case SyntaxKind.ExclamationEqualsToken:
3397833978
case SyntaxKind.EqualsEqualsEqualsToken:
3397933979
case SyntaxKind.ExclamationEqualsEqualsToken:
33980+
if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) {
33981+
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
33982+
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
33983+
}
3398033984
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
3398133985
return booleanType;
3398233986

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3459,6 +3459,10 @@
34593459
"category": "Error",
34603460
"code": 2838
34613461
},
3462+
"This condition will always return '{0}' since JavaScript compares objects by reference, not value.": {
3463+
"category": "Error",
3464+
"code": 2839
3465+
},
34623466

34633467
"Import declaration '{0}' is using private name '{1}'.": {
34643468
"category": "Error",

src/compiler/utilitiesPublic.ts

+13
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,19 @@ namespace ts {
11141114
return isLiteralKind(node.kind);
11151115
}
11161116

1117+
/** @internal */
1118+
export function isLiteralExpressionOfObject(node: Node) {
1119+
switch (node.kind) {
1120+
case SyntaxKind.ObjectLiteralExpression:
1121+
case SyntaxKind.ArrayLiteralExpression:
1122+
case SyntaxKind.RegularExpressionLiteral:
1123+
case SyntaxKind.FunctionExpression:
1124+
case SyntaxKind.ClassExpression:
1125+
return true;
1126+
}
1127+
return false;
1128+
}
1129+
11171130
// Pseudo-literals
11181131

11191132
/* @internal */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(4,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
2+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(6,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
3+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(8,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
4+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(10,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
5+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(12,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
6+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(14,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
7+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(17,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
8+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(19,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
9+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(21,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
10+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(23,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
11+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(25,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
12+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(27,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
13+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(30,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
14+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(32,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
15+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(34,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
16+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(36,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
17+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(38,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
18+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(40,5): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
19+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(43,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
20+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(45,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
21+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(47,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
22+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(49,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
23+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(51,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
24+
tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts(53,5): error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
25+
26+
27+
==== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts (24 errors) ====
28+
const a = { a: 1 }
29+
const b = [1]
30+
31+
if ({ a: 1 } === { a: 1 }) {
32+
~~~~~~~~~~~~~~~~~~~~~
33+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
34+
}
35+
if ([1] === [1]) {
36+
~~~~~~~~~~~
37+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
38+
}
39+
if (a === { a: 1 }) {
40+
~~~~~~~~~~~~~~
41+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
42+
}
43+
if (b === [1]) {
44+
~~~~~~~~~
45+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
46+
}
47+
if ({ a: 1 } === a) {
48+
~~~~~~~~~~~~~~
49+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
50+
}
51+
if ([1] === b) {
52+
~~~~~~~~~
53+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
54+
}
55+
56+
if ({ a: 1 } !== { a: 1 }) {
57+
~~~~~~~~~~~~~~~~~~~~~
58+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
59+
}
60+
if ([1] !== [1]) {
61+
~~~~~~~~~~~
62+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
63+
}
64+
if (a !== { a: 1 }) {
65+
~~~~~~~~~~~~~~
66+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
67+
}
68+
if (b !== [1]) {
69+
~~~~~~~~~
70+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
71+
}
72+
if ({ a: 1 } !== a) {
73+
~~~~~~~~~~~~~~
74+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
75+
}
76+
if ([1] !== b) {
77+
~~~~~~~~~
78+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
79+
}
80+
81+
if ({ a: 1 } == { a: 1 }) {
82+
~~~~~~~~~~~~~~~~~~~~
83+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
84+
}
85+
if ([1] == [1]) {
86+
~~~~~~~~~~
87+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
88+
}
89+
if (a == { a: 1 }) {
90+
~~~~~~~~~~~~~
91+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
92+
}
93+
if (b == [1]) {
94+
~~~~~~~~
95+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
96+
}
97+
if ({ a: 1 } == a) {
98+
~~~~~~~~~~~~~
99+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
100+
}
101+
if ([1] == b) {
102+
~~~~~~~~
103+
!!! error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
104+
}
105+
106+
if ({ a: 1 } != { a: 1 }) {
107+
~~~~~~~~~~~~~~~~~~~~
108+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
109+
}
110+
if ([1] != [1]) {
111+
~~~~~~~~~~
112+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
113+
}
114+
if (a != { a: 1 }) {
115+
~~~~~~~~~~~~~
116+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
117+
}
118+
if (b != [1]) {
119+
~~~~~~~~
120+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
121+
}
122+
if ({ a: 1 } != a) {
123+
~~~~~~~~~~~~~
124+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
125+
}
126+
if ([1] != b) {
127+
~~~~~~~~
128+
!!! error TS2839: This condition will always return 'true' since JavaScript compares objects by reference, not value.
129+
}
130+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//// [conditionalEqualityOnLiteralObjects.ts]
2+
const a = { a: 1 }
3+
const b = [1]
4+
5+
if ({ a: 1 } === { a: 1 }) {
6+
}
7+
if ([1] === [1]) {
8+
}
9+
if (a === { a: 1 }) {
10+
}
11+
if (b === [1]) {
12+
}
13+
if ({ a: 1 } === a) {
14+
}
15+
if ([1] === b) {
16+
}
17+
18+
if ({ a: 1 } !== { a: 1 }) {
19+
}
20+
if ([1] !== [1]) {
21+
}
22+
if (a !== { a: 1 }) {
23+
}
24+
if (b !== [1]) {
25+
}
26+
if ({ a: 1 } !== a) {
27+
}
28+
if ([1] !== b) {
29+
}
30+
31+
if ({ a: 1 } == { a: 1 }) {
32+
}
33+
if ([1] == [1]) {
34+
}
35+
if (a == { a: 1 }) {
36+
}
37+
if (b == [1]) {
38+
}
39+
if ({ a: 1 } == a) {
40+
}
41+
if ([1] == b) {
42+
}
43+
44+
if ({ a: 1 } != { a: 1 }) {
45+
}
46+
if ([1] != [1]) {
47+
}
48+
if (a != { a: 1 }) {
49+
}
50+
if (b != [1]) {
51+
}
52+
if ({ a: 1 } != a) {
53+
}
54+
if ([1] != b) {
55+
}
56+
57+
58+
//// [conditionalEqualityOnLiteralObjects.js]
59+
var a = { a: 1 };
60+
var b = [1];
61+
if ({ a: 1 } === { a: 1 }) {
62+
}
63+
if ([1] === [1]) {
64+
}
65+
if (a === { a: 1 }) {
66+
}
67+
if (b === [1]) {
68+
}
69+
if ({ a: 1 } === a) {
70+
}
71+
if ([1] === b) {
72+
}
73+
if ({ a: 1 } !== { a: 1 }) {
74+
}
75+
if ([1] !== [1]) {
76+
}
77+
if (a !== { a: 1 }) {
78+
}
79+
if (b !== [1]) {
80+
}
81+
if ({ a: 1 } !== a) {
82+
}
83+
if ([1] !== b) {
84+
}
85+
if ({ a: 1 } == { a: 1 }) {
86+
}
87+
if ([1] == [1]) {
88+
}
89+
if (a == { a: 1 }) {
90+
}
91+
if (b == [1]) {
92+
}
93+
if ({ a: 1 } == a) {
94+
}
95+
if ([1] == b) {
96+
}
97+
if ({ a: 1 } != { a: 1 }) {
98+
}
99+
if ([1] != [1]) {
100+
}
101+
if (a != { a: 1 }) {
102+
}
103+
if (b != [1]) {
104+
}
105+
if ({ a: 1 } != a) {
106+
}
107+
if ([1] != b) {
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/conditionalEqualityOnLiteralObjects.ts ===
2+
const a = { a: 1 }
3+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
4+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 11))
5+
6+
const b = [1]
7+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
8+
9+
if ({ a: 1 } === { a: 1 }) {
10+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 5))
11+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 3, 18))
12+
}
13+
if ([1] === [1]) {
14+
}
15+
if (a === { a: 1 }) {
16+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
17+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 7, 11))
18+
}
19+
if (b === [1]) {
20+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
21+
}
22+
if ({ a: 1 } === a) {
23+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 11, 5))
24+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
25+
}
26+
if ([1] === b) {
27+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
28+
}
29+
30+
if ({ a: 1 } !== { a: 1 }) {
31+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 5))
32+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 16, 18))
33+
}
34+
if ([1] !== [1]) {
35+
}
36+
if (a !== { a: 1 }) {
37+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
38+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 20, 11))
39+
}
40+
if (b !== [1]) {
41+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
42+
}
43+
if ({ a: 1 } !== a) {
44+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 24, 5))
45+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
46+
}
47+
if ([1] !== b) {
48+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
49+
}
50+
51+
if ({ a: 1 } == { a: 1 }) {
52+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 5))
53+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 29, 17))
54+
}
55+
if ([1] == [1]) {
56+
}
57+
if (a == { a: 1 }) {
58+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
59+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 33, 10))
60+
}
61+
if (b == [1]) {
62+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
63+
}
64+
if ({ a: 1 } == a) {
65+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 37, 5))
66+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
67+
}
68+
if ([1] == b) {
69+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
70+
}
71+
72+
if ({ a: 1 } != { a: 1 }) {
73+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 5))
74+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 42, 17))
75+
}
76+
if ([1] != [1]) {
77+
}
78+
if (a != { a: 1 }) {
79+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
80+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 46, 10))
81+
}
82+
if (b != [1]) {
83+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
84+
}
85+
if ({ a: 1 } != a) {
86+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 50, 5))
87+
>a : Symbol(a, Decl(conditionalEqualityOnLiteralObjects.ts, 0, 5))
88+
}
89+
if ([1] != b) {
90+
>b : Symbol(b, Decl(conditionalEqualityOnLiteralObjects.ts, 1, 5))
91+
}
92+

0 commit comments

Comments
 (0)