Improve your Javascript conditionals
Tags: javascript, functional, programming, codequality
Published: 2021-07-25 16:37:33

Writing conditionals are unavoidable when building software. It is one of the key topics when learning Javascript. However, conditionals are also the biggest contributor to push our software into entropy. It is important to be clear and explicit with our conditionals to ensure the quality of our code is maintained.

Modern Javascript provide us a vast arsenal of tools and methods to help structure our code. Here are some tips to improve your Javascript conditionals:
___

###1. Name your condition
The first and maybe the most impactful thing you can do to improve your code is naming things correctly, that includes when dealing with conditionals. After all, 

> There are only two hard things in Computer Science: cache invalidation and naming things.
> - Phil Karlton

Be explicit with what you're checking for when writing an if statement, especially so if its a multi-condition check. You may get a pass if its a short one liner with single condition.

######BAD
```javascript
if (item.group === 'fruits' && item.name === 'apple' && item.status === 'ripe') {
  // slice my apple
}
```

######GOOD
```javascript
const isAppleRipe = item.group === 'fruits' && item.name === 'apple' && item.status === 'ripe';

if (isAppleRipe) {
  // slice my apple
}
```

Another quick tip here: naming boolean type variable, start it with "is", or "should" or "has", that are intuitively of boolean nature.
___

###2. Type coercion
Javascript is a dynamically typed language, that means variable in Javascript can switch data type on the fly if you're not careful. Understand what are [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) and [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) values, and type cast your conditional check into boolean using `!` and `!!` (single and double exclamation). This is especially useful when writing Typescript that are type sensitive when return values. 

```javascript
const list = [];

const isEmpty = !list.length;
const isNotEmpty = !!list.length;
```

In most cases, you want to avoid explicitly checking for type. Ultimately it depends on the requirement, maybe in certain cases you want to be really explicit that you're checking for undefined, or null, but usually you can get away with simply casting them into boolean.

```javascript
if (!isAvailable) {
  // do your thing
}

// instead of

if (isAvailable === null || isAvailable === undefined || isAvailable === false) {
  // do your thing
}
```

If you're using Typescript, you should instead leverage on it's capability by declaring variable type along side with the variable. Otherwise, default parameters in javascript can also help your case here. Design your software in a way where falsy or truthy value is expected.

######BAD
```javascript
function doYourThing = (isActive) => {
  if (isActive !== null || isActive !== undefined || isActive !== false) {
    ...
  }
}
```

######GOOD
```javascript
function doYourThing = (isActive: boolean = false) => {
  if (isActive) {
    ...
  }
}
```

In cases where we'd check for object property value, we're used to a long list of condition chaining, ie. `item && item.name` to avoid nullish reference error. We can now use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) when checking for object property, which would return `undefined` if it is not available.

######BAD
```javascript
if (item && item.name) { ... }
```

######GOOD
```javascript
if (!!item?.name) { ... }
```
___

###3. Guard clause
Guard clause is a fancy way to tell you to always return early. Write your conditions to exit a function, rather than conditions to enter a function, to put it succinctly:

######BAD
```javascript
function getUserSession(user) {
  if (!!user.name && !!user.password) {
    // proceed to get user session
  }
}
```

######GOOD
```javascript
function getUserSession(user) {
  if (!user.name || !user.password) {
    return;
  }

  // proceed to get user session
}
```

By exiting a function early with guard clause, you get the invalid cases out of the way first (the bouncer pattern), before proceeding with the "real" body of your function. This will effectively reduce your code indentation caused by multi-level nested `if else` statements that are hard to read and hard to edit.

Mentally it also helps your fellow developers to jump to the next function earlier without needing to read through the whole body of function.
___

###4. Conditional operators
A lot of the times we need to assign values or call a function based on condition. We can use conditional operators to make our code a little cleaner and easier to follow. Which conditional operator to use will depend on the requirement.

When declaring a variable, we should minimize the need for re-assignment to help with predictability of your code. In the same spirit, it is good to prefer const when declaring a variable. 

In cases where the value of the variable can be different if condition is true or false, we can use [ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) to help shorten our code:

######BAD
```javascript
let itemGroup;

if (itemType === 'apple') {
  itemGroup = 'fruit';
} else {
  itemGroup = 'vegetable';
}
```

######GOOD
```javascript
const itemGroup = itemType === 'apple' ? 'fruit' : 'vegetable';
```

However, be mindful to not overdo it with multiple nested ternary operator. In cases where that is required, consider restructuring your logic or use one of the methods mentioned later in the article to handle conditionals with multiple outs.

In cases where we need to assign value if value is falsy, we can use OR `||` operator. If we want to be more explicit with our checking, to target only `null` or `undefined` we can use [nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) operator. 

```javascript
const itemNameFalsyCheck = item.name || 'Name is falsy';

const itemNameNullOrUndefinedCheck = item.name ?? 'Name is null or undefined';
```
___

###5. List check
There are many scenarios where we are dealing with a list of values, and we'll want to check for something in the list, either if value exist, or value is of certain type, etc.

If we're checking for multiple values we can use Array method `.includes` or Set method `.has` instead of chaining multiple OR operators.

######BAD
```javascript
const isFruit = (item.type === 'apple' || item.type === 'orange' || item.type === 'durian');

if (isFruit) { ... }
```

######GOOD
```javascript
const isFruit = ['apple', 'orange', 'durian'].includes(item.type);

// or

const isFruit = new Set(['apple', 'orange', 'durian']).has(item.type);
```

It is worth nothing that `Set.has` has a significant performance edge over `Array.includes`, especially when dealing with a [large data set](https://www.tech-hour.com/javascript-performance-and-optimization), it is worth fitting Set checking in your code when possible.

However in cases where Set has to be repeatedly re-initialized (ie. in a loop), the cost of initializing Set will be too big, the offset will result in worse performance, and in such cases it is better to stick with `Array.includes`.

######BAD
```javascript
const listOfFruits = list.filter(x => {
  const fruits = new Set(['apple', 'orange', 'durian']);
  return fruits.has(x);
});
```

######GOOD
```javascript
const listOfFruits = list.filter(x => ['apple', 'orange', 'durian'].includes(x));
```

If we're checking for any of the items in a list is of certain value, we can use `Array.some`:

```javascript
const hasFruits = list.some(x => x.type === 'fruit');
```

If we're checking whether all of the items in a list if of certain value, we can use `Array.every`:
```javascript
const itsAllFruits = list.every(x => x.type === 'fruit');
```
___

###6. Multiple outs
In cases where we are checking for more than 2 outs, there are multiple ways to handle them without the need for multiple `if else` statement. It is in fact best to avoid multiple `if else` as it is notoriously hard to read, hard to edit, and also slower in performance in comparison to the few options that we have. They are namely, `switch` statements, `object` literals, and `Map`.

######BAD
```javascript
let itemGroup;

if (item.name === 'apple') {
  itemGroup = 'fruit';
} else if (item.name === 'carrot') {
  itemGroup = 'vegetable';
} else if (item.name === 'mazda') {
  itemGroup = 'car';
} else {
  itemGroup = 'fruit';
}

```

######GOOD
```javascript
let itemGroup;

// with switch
switch (item.name) {
  case 'carrot':
    itemGroup = 'vegetable';
    return;
  case 'mazda':
    itemGroup = 'car';
    return;
  case 'apple':
  default:
    itemGroup = 'fruit';
    return;
}

// with object
itemGroup = {
  apple: 'fruit',
  carrot: 'vegetable',
  mazda: 'car'
}[item.type] ?? 'fruit';

// with Map
itemGroup = Map()
  .set('apple', 'fruit')
  .set('carrot', 'vegetable')
  .set('mazda', 'car')
  .get(item.type) ?? 'fruit';
```

We can also use [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) with switch to make our switch statement much more readable. Using IIFE to handle switch cases also open up your conditional check for data massaging before returning. Just be mindful and not overdo it, keep it small and simple.

```javascript
const itemGroup = ((type) => {
  case 'carrot':
    return 'vegetable';
  case 'mazda':
    return 'car';
  case 'apple':
  default:
    return 'fruit';
})(item.type);
```
___

## Conclusion
Be mindful when writing conditionals in your code. It is one of the foundation to becoming a solid Javascript developer. One last tip I have is to extract your commonly used check / validations into small utility functions. They are easily readable, testable, which will result in a less error prone system. That is all that I have to share when it comes to improving your Javascript conditionals. Happy coding!