eslint-plugin-unicorn icon indicating copy to clipboard operation
eslint-plugin-unicorn copied to clipboard

Rule proposal: `no-unnecessary-negation`

Open noftaly opened this issue 5 years ago • 20 comments

Description

This rule would enforce users to simplify negations: !(a === b) -> a !== b. There might be other conditions like this that could be simplified.

Fail

if (!(a > b)) {}
const foo = !(bar === baz);
const foo = !(typeof bar === 'undefined');

Pass

if (a <= b) {}
const foo = bar !== baz;
const foo = typeof bar !== 'undefined';
const foo = !Array.isArray(bar);

noftaly avatar Oct 12 '20 10:10 noftaly

I love this one, it will be great if we can convert big nested LogicExpressions.

fisker avatar Oct 12 '20 11:10 fisker

"Fun" fact: !(a > b) is not strictly equal to (a <= b)...

console.log(!(null > undefined)); // true
console.log(null <= undefined); // false

papb avatar Oct 12 '20 14:10 papb

What about 2 conditions?

	if (!(a===b && c === d)) {}

Sometimes that way is easier to read

yakov116 avatar Oct 12 '20 14:10 yakov116

Sometimes that way is easier to read

I agree; some times the non-DeMorgan way is more readable

papb avatar Oct 12 '20 23:10 papb

Then maybe add an option to not optimize when there are multiple ANDs, or nested ANDs and ORs? Or we also just could not add an option and let the user disable the rule inline.

Concerning the implementation, we could use some boolean-expressions optimization algorithms, and check if it is shorter than the original one, and if it is, replace? This will only work with boolean operators tho, not >. But do we really want to support that? As @papb said, fixing it might break the code

noftaly avatar Oct 13 '20 11:10 noftaly

if (!(a===b && c === d)) {}

I much prefer this too. If there's going to be an option, it should prefer this by default.

sindresorhus avatar Dec 10 '20 13:12 sindresorhus

But do we really want to support that? As @papb said, fixing it might break the code

Yes, but they should not be auto-fixable. We can make them use the suggestions API instead.

sindresorhus avatar Dec 10 '20 14:12 sindresorhus

we could use some boolean-expressions optimization algorithms

Do you have any in mind?

sindresorhus avatar Dec 10 '20 14:12 sindresorhus

Do you have any in mind?

Not really, and I searched online and did not found any in JS. But I was thinking of something like this: boolean-algebra It will, for example, simplify an expression like this image to this : !A + !B

noftaly avatar Dec 10 '20 14:12 noftaly

This is now accepted.

It should prefer readability by default, for example: if (!(a === b && c === d)) {}.

sindresorhus avatar Jan 11 '21 07:01 sindresorhus

Similar rule https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-inverted-boolean-check.md

fisker avatar Jan 11 '21 15:01 fisker

this: boolean-algebra It will, for example, simplify an expression like this

I was thinking about a rule that did this exactly: simplify-boolean-algebra or something like that.

Instead of manually handling each boolean-related awkwardness, why not just simplify whatever expression we see?

I regularly need this, however it's usually across if/else/elseif/nested-if/return rather than in one if (expression)

fregante avatar Jan 17 '21 10:01 fregante

The code is not in a presentable state, but it works 🎉

https://astexplorer.net/#/gist/c922ca1591e8db763030071c09df875a/3865211cf9b06a03034a0b199d0e65f6ff8f49c5

Please let me know when you find something that does not work as you expect and generally what you have tried out, so we can include that in our test cases.

jguddas avatar Sep 28 '22 16:09 jguddas

What about something like a - -b which can be simplified down to a + b, should that be included in this rule?

I feel like it fits a lot more in the scope of this rule than the prefer-simplified-expression stuff.

jguddas avatar Oct 23 '22 18:10 jguddas

You have to be careful simplifying expressions with - and + because of automatic type inference. There are edge cases where a - -b is not the same as a + b

> 3 - -[]
3
> 3 + []
'3'
> 3 - -{}
NaN
> 3 + {}
'3[object Object]'
> 3 - -Number
NaN
> 3 + Number
'3function Number() { [native code] }'

noftaly avatar Oct 23 '22 19:10 noftaly

You are right, what about fixing 3 - -{} to {} + 3, that would work?

jguddas avatar Oct 24 '22 07:10 jguddas

There is a rust version https://github.com/rome/tools/blob/22dd11d5d03486d3b6f6679f398cf0bbc440a2ef/crates/rome_js_analyze/src/analyzers/complexity/use_simplified_logic_expression.rs

fisker avatar Nov 09 '22 07:11 fisker

if (!(a === b && c === d)) {}

become

if(a !== b || c !== d) {}

yukulele avatar Dec 07 '22 01:12 yukulele

@yukulele my solution was to add a reduceParens option that is disabled by default so you would only get that case if you want it to, by default it would stay !(a === b && c === d).

jguddas avatar Dec 07 '22 07:12 jguddas

Recently came upon this: https://github.com/azat-io/eslint-plugin-de-morgan

yusufkandemir avatar Feb 10 '25 11:02 yusufkandemir