eslint-plugin-no-object-comparison
v1.0.0
Published
ESLint rule to prevent comparing objects using comparison operators
Maintainers
Readme
eslint-plugin-no-object-comparison
An ESLint plugin that prevents unsafe comparisons in TypeScript. Uses the type system to detect when comparisons will fail silently.
Installation
pnpm add -D eslint-plugin-no-object-comparisonRequirements: 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.
Quick Start
// 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,
];Rules Overview
| Rule | Description | Recommended |
|------|-------------|:-----------:|
| object-equality | Prevent === on objects | error |
| object-relational | Prevent < > on objects | error |
| loose-type | Prevent comparisons with any | error |
| nan-comparison | Prevent x === NaN | error |
| self-comparison | Prevent x !== x | error |
| floating-point-equality | Prevent 0.1 + 0.2 === 0.3 | error |
| string-number-relational | Prevent '10' < '2' | error |
| symbol-literal-comparison | Prevent Symbol() === Symbol() | error |
| object-literal-switch-case | Prevent case {id:1}: | error |
| boxed-primitive-comparison | Prevent new String() === new String() | error |
| array-includes-object | Prevent arr.includes({}) | warn |
| collection-reference-lookup | Prevent map.get({}) | warn |
| collection-object-key | Prevent map.set(obj, val) | warn |
| json-stringify-comparison | Prevent JSON.stringify() comparisons | warn |
Rules
object-equality
Prevents comparing objects with === or !==. Objects are compared by reference, not value.
// Bad - always false (different references)
if (user1 === user2) { }
if ({ id: 1 } === { id: 1 }) { }
// Good
if (user1.id === user2.id) { }object-relational
Prevents relational comparisons (<, >, <=, >=) on objects without a valueOf method.
// Bad - objects coerce to '[object Object]'
if (user1 < user2) { }
// Good - compare primitive properties
if (user1.age < user2.age) { }loose-type
Prevents comparisons involving any or unknown types, which bypass type safety.
// 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) { }nan-comparison
Prevents comparing with NaN using equality operators. NaN is not equal to anything, including itself.
// Bad - always false
if (x === NaN) { }
if (x !== NaN) { } // always true
// Good
if (Number.isNaN(x)) { }self-comparison
Prevents comparing a variable to itself, which is often an opaque NaN check or a typo.
// Bad - opaque NaN check
if (x !== x) { }
// Good - explicit NaN check
if (Number.isNaN(x)) { }floating-point-equality
Prevents strict equality on floating-point arithmetic, which suffers from precision issues.
// 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) { }string-number-relational
Prevents relational comparisons on numeric strings, which compare lexicographically.
// Bad - '9' > '10' is true (lexicographic)
if ('10' < '2') { }
// Good - convert to numbers
if (Number('10') < Number('2')) { }symbol-literal-comparison
Prevents comparing Symbol() calls. Each call creates a unique symbol.
// Bad - always false (unique symbols)
if (Symbol('id') === Symbol('id')) { }
// Good - use Symbol.for() for shared symbols
if (Symbol.for('id') === Symbol.for('id')) { }object-literal-switch-case
Prevents object literals in switch cases. Switch uses === which compares by reference.
// Bad - case never matches
switch (value) {
case { type: 'admin' }: break;
}
// Good - compare primitives
switch (value.type) {
case 'admin': break;
}boxed-primitive-comparison
Prevents comparing boxed primitives (new String, new Number, new Boolean), which are objects.
// Bad - false (different objects)
new String('a') === new String('a')
// Good - use primitives
'a' === 'a'
String(x) === String(y)array-includes-object
Prevents using includes(), indexOf(), lastIndexOf() with object literals or new expressions.
// 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)collection-reference-lookup
Prevents using object literals in Map/Set lookup methods (get, has, delete).
// 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); // workscollection-object-key
Prevents using non-primitive keys in Map.set() and Set.add(). Object keys require keeping the exact reference.
// 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')json-stringify-comparison
Prevents comparing JSON.stringify() results. Key order isn't guaranteed and some values are dropped.
// Bad - unreliable
JSON.stringify(a) === JSON.stringify(b)
// Good - use deep equality library
import { isEqual } from 'lodash';
isEqual(a, b)Configs
| 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
recommended
Core rules as errors, optional rules as warnings. Includes indirectReferences: 'warn' for lookup rules. Best for most projects.
noObjectComparison.configs.recommendedstrict
All rules as errors, with indirectReferences: 'error' for lookup rules. For maximum safety.
noObjectComparison.configs.strictInline Suppression
// eslint-disable-next-line no-object-comparison/object-equality
if (obj1 === obj2) { }License
MIT
