ESLint rule to prevent comparing objects using comparison operators
npm install eslint-plugin-no-object-comparisonAn ESLint plugin that prevents unsafe comparisons in TypeScript. Uses the type system to detect when comparisons will fail silently.
``bash`
pnpm add -D eslint-plugin-no-object-comparison
Requirements: ESLint >= 8, TypeScript >= 5, @typescript-eslint/parser >= 6
Important: This plugin requires typed linting to be enabled. You must configure parserOptions.project to point to your tsconfig.json.
`javascript
// eslint.config.js
import tsParser from '@typescript-eslint/parser';
import noObjectComparison from 'eslint-plugin-no-object-comparison';
export default [
{
files: ['/.ts', '/.tsx'],
languageOptions: {
parser: tsParser,
parserOptions: { project: './tsconfig.json' },
},
},
noObjectComparison.configs.recommended,
];
`
| Rule | Description | Recommended |
|------|-------------|:-----------:|
| object-equality | Prevent === on objects | error |<
| object-relational | Prevent > on objects | error |any
| loose-type | Prevent comparisons with | error |x === NaN
| nan-comparison | Prevent | error |x !== x
| self-comparison | Prevent | error |0.1 + 0.2 === 0.3
| floating-point-equality | Prevent | error |'10' < '2'
| string-number-relational | Prevent | error |Symbol() === Symbol()
| symbol-literal-comparison | Prevent | error |case {id:1}:
| object-literal-switch-case | Prevent | error |new String() === new String()
| boxed-primitive-comparison | Prevent | error |arr.includes({})
| array-includes-object | Prevent | warn |map.get({})
| collection-reference-lookup | Prevent | warn |map.set(obj, val)
| collection-object-key | Prevent | warn |JSON.stringify()
| json-stringify-comparison | Prevent comparisons | warn |
---
Prevents comparing objects with === or !==. Objects are compared by reference, not value.
`typescript
// Bad - always false (different references)
if (user1 === user2) { }
if ({ id: 1 } === { id: 1 }) { }
// Good
if (user1.id === user2.id) { }
`
---
Prevents relational comparisons (<, >, <=, >=) on objects without a valueOf method.
`typescript
// Bad - objects coerce to '[object Object]'
if (user1 < user2) { }
// Good - compare primitive properties
if (user1.age < user2.age) { }
`
---
Prevents comparisons involving any or unknown types, which bypass type safety.
`typescript
// Bad - type system can't verify this
const data: any = fetchData();
if (data === user) { }
// Good - narrow the type first
if (isUser(data) && data.id === user.id) { }
`
---
Prevents comparing with NaN using equality operators. NaN is not equal to anything, including itself.
`typescript
// Bad - always false
if (x === NaN) { }
if (x !== NaN) { } // always true
// Good
if (Number.isNaN(x)) { }
`
---
Prevents comparing a variable to itself, which is often an opaque NaN check or a typo.
`typescript
// Bad - opaque NaN check
if (x !== x) { }
// Good - explicit NaN check
if (Number.isNaN(x)) { }
`
---
Prevents strict equality on floating-point arithmetic, which suffers from precision issues.
`typescript
// Bad - false due to IEEE 754
if (0.1 + 0.2 === 0.3) { }
// Good - use epsilon comparison
if (Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON) { }
`
---
Prevents relational comparisons on numeric strings, which compare lexicographically.
`typescript
// Bad - '9' > '10' is true (lexicographic)
if ('10' < '2') { }
// Good - convert to numbers
if (Number('10') < Number('2')) { }
`
---
Prevents comparing Symbol() calls. Each call creates a unique symbol.
`typescript
// Bad - always false (unique symbols)
if (Symbol('id') === Symbol('id')) { }
// Good - use Symbol.for() for shared symbols
if (Symbol.for('id') === Symbol.for('id')) { }
`
---
Prevents object literals in switch cases. Switch uses === which compares by reference.
`typescript
// Bad - case never matches
switch (value) {
case { type: 'admin' }: break;
}
// Good - compare primitives
switch (value.type) {
case 'admin': break;
}
`
---
Prevents comparing boxed primitives (new String, new Number, new Boolean), which are objects.
`typescript
// Bad - false (different objects)
new String('a') === new String('a')
// Good - use primitives
'a' === 'a'
String(x) === String(y)
`
---
Prevents using includes(), indexOf(), lastIndexOf() with object literals or new expressions.
`typescript
// Bad - never finds (different reference)
users.includes({ id: 1 })
arr.includes(new User())
// Good - use find/some
users.find(u => u.id === 1)
users.some(u => u.id === 1)
`
---
Prevents using object literals in Map/Set lookup methods (get, has, delete).
`typescript
// Bad - never finds (different reference)
map.get({ id: 1 })
set.has({ id: 1 })
// Good - use stored reference
const key = { id: 1 };
map.set(key, 'value');
map.get(key); // works
`
---
Prevents using non-primitive keys in Map.set() and Set.add(). Object keys require keeping the exact reference.
`typescript
// Bad - error-prone, must keep reference
map.set(user, 'data')
set.add(config)
// Good - use primitive keys
map.set(user.id, 'data')
// Good - use WeakMap for object keys
weakMap.set(user, 'data')
`
---
Prevents comparing JSON.stringify() results. Key order isn't guaranteed and some values are dropped.
`typescript
// Bad - unreliable
JSON.stringify(a) === JSON.stringify(b)
// Good - use deep equality library
import { isEqual } from 'lodash';
isEqual(a, b)
`
---
| Rule | recommended | strict |
|------|:-----------:|:------:|
| object-equality | š“ | š“ |
| object-relational | š“ | š“ |
| loose-type | š“ | š“ |
| nan-comparison | š“ | š“ |
| self-comparison | š“ | š“ |
| floating-point-equality | š“ | š“ |
| string-number-relational | š“ | š“ |
| symbol-literal-comparison | š“ | š“ |
| object-literal-switch-case | š“ | š“ |
| boxed-primitive-comparison | š“ | š“ |
| array-includes-object | š” | š“ |
| collection-reference-lookup | š” | š“ |
| collection-object-key | š” | š“ |
| json-stringify-comparison | š” | š“ |
š“ error š” warn
Core rules as errors, optional rules as warnings. Includes indirectReferences: 'warn' for lookup rules. Best for most projects.
`javascript`
noObjectComparison.configs.recommended
All rules as errors, with indirectReferences: 'error' for lookup rules. For maximum safety.
`javascript`
noObjectComparison.configs.strict
`typescript``
// eslint-disable-next-line no-object-comparison/object-equality
if (obj1 === obj2) { }
MIT