JavaScript: Not-Not (!!) Anti-Pattern

bob.ts - May 23 '22 - - Dev Community

The Not-Not Pattern (!!), or bang-bang, is a way in JavaScript to do a type conversion.

! means NOT. So ...

  • !true is false
  • !false is true
  • !0 is true
  • !1 is false

So when converting a value to a boolean, the code inverts it and inverts it again.

Examples

          !!false === false
           !!true === true

              !!0 === false
!!parseInt("foo") === false // NaN is falsy
              !!1 === true
             !!-1 === true  // -1 is truthy
          !!(1/0) === true  // Infinity is truthy

             !!"" === false // empty string is falsy
          !!"foo" === true  // non-empty string is truthy
        !!"false" === true  // even if it contains a falsy value

     !!window.foo === false // undefined value is falsy
      !!undefined === false // undefined primitive is falsy
           !!null === false // null is falsy

             !!{} === true  // an (empty) object is truthy
             !![] === true  // an (empty) array is truthy
Enter fullscreen mode Exit fullscreen mode

In the Real World

If those examples weren't enough to get you to think about this pattern differently, you should know that this pattern can have some significant and unexpected side-effects.

A codebase I was working in loaded information on images on the page. We had a process that needed to run if an image was returned. The thought behind this code was that it should not run if there was an error.

Six developers and months of time were spent trying to track down a bug. This bug showed up one in every 3,000 times the information on an image was loaded.

The Code

Patience and persistence on the part of an intern revealed a significant side effect.

const image = { name: 'BOB.jpg' };

function checkImage(image) {
  if (!!image) {
    console.log('image exists');
  }
}
checkImage(image);
Enter fullscreen mode Exit fullscreen mode

So, the original code looked as you see above. an image object was passed in and if it exists, the console.log is run.

Unexpected Side-Effect

Here's where the side-effect came in ...

const image = "ERROR: Authentication Wrong";

function checkImage(image) {
  if (!!image) {
    console.log('image exists');
  }
}
checkImage(image);
Enter fullscreen mode Exit fullscreen mode

With the code above, the intern saw that when an error occurred, the Not-Not Pattern saw the error message at true and it tried to process as if there was an image.

A Solution

The solution wasn't challenging.

We needed a better pattern for checking the image object. This is one solution; there can be many more.

const image = "ERROR: Authentication Wrong";

function isObject(val) {
  if (val === null) { return false;}
  return ( (typeof val === 'function') || (typeof val === 'object') );
}

function checkImage(image) {
  if (isObject(image) === true) {
    console.log('image exists');
  }
}
checkImage(image);
Enter fullscreen mode Exit fullscreen mode

With the code above, the console.log does not run.

Summary

The Not-Not Pattern (!!), or bang-bang, is a way in JavaScript to do a type conversion. This pattern can have some significant and unexpected side-effects.

Because of these side effects I am reluctant to use the pattern in most cases and write code that is longer, with more clarity of purpose, and less potential side-effects.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .