DEV Community

Cover image for Revisiting Conditionals in JavaScript and TypeScript
TK
TK

Posted on • Originally published at iamtk.co

Revisiting Conditionals in JavaScript and TypeScript

The original post was published at iamtk.co.

This post is part of the Mastering JavaScript Series.

We are going to see different ways we can handle conditions in JavaScript and how TypeScript can help us make better use of code.

Imagine we have a boolean value and based on this boolean, we want to assign a value to a new variable.

const isActive = true;
Enter fullscreen mode Exit fullscreen mode

With this boolean, we want:

  • if active (isActive = true): assign a value on to the variable toggle.
  • if inactive (isActive = false): assign a value off to the variable toggle.
let toggle;

if (isActive) {
    toggle = 'on';
} else {
    toggle = 'off';
}
Enter fullscreen mode Exit fullscreen mode

To do this, we usually use a var or a let statement. Create a toggle with undefined value and then assign the correct value based on the isActive value.

This works.

But we can't use const in this case. When defining a const, we need to add a value attached to it. Doing something like this will throw an error:

> Uncaught SyntaxError: Missing initializer in const declaration
Enter fullscreen mode Exit fullscreen mode

We also can't use const inside the if-else condition.

If we do this:

if (isActive) {
    const toggle = 'on';
} else {
    const toggle = 'off';
}
Enter fullscreen mode Exit fullscreen mode

And then verify the toggle value, it throws an error because this constant is not in the scope.

$ toggle
> Uncaught ReferenceError: toggle is not defined
Enter fullscreen mode Exit fullscreen mode

Another way to handle this type of condition is by using the ternary operator.

const toggle = isActive ? 'on' : 'off';
Enter fullscreen mode Exit fullscreen mode

That's nice and beautiful. Capture everything in a very short and readable way.

Now imagine handling multiple conditions. We can't really use the ternary operator. The first thought is to come back to the if-else statement, but now with multiple possible conditions:

let label;

if (status === 'finished') {
    label = 'Finished task';
} else if (status === 'inactive') {
    label = 'Task inactive';
} else if (status === 'ongoing') {
    label = 'Ongoing task';
}
Enter fullscreen mode Exit fullscreen mode

Another possibility that comes to mind is using a switch case.

let label;

switch (status) {
    case 'finished':
        label = 'Finished task';
        break;
    case 'inactive':
        label = 'Task inactive';
        break;
    case 'ongoing':
        label = 'Ongoing task';
        break;
}
Enter fullscreen mode Exit fullscreen mode

But what if we also want to assign a value to another variable? A tag variable in this case. The tag's value follows this logic:

  • finished: Finished
  • inactive: Inactive
  • ongoing: Ongoing

Let's build it!

let label;
let tag;

switch (status) {
    case 'finished':
        label = 'Finished task';
        tag = 'Finished';
        break;
    case 'inactive':
        label = 'Task inactive';
        tag = 'Inactive';
        break;
    case 'ongoing':
        label = 'Ongoing task';
        tag = 'Ongoing';
        break;
}
Enter fullscreen mode Exit fullscreen mode

Now we also want a button's variant for each status. The logic follows:

  • finished: secondary
  • inactive: disabled
  • ongoing: primary

Let's add this variable to the switch case.

let label;
let tag;
let variant;

switch (status) {
  case 'finished':
    label = 'Finished task';
    tag = 'Finished';
    variant = 'secondary';
    break;
  case 'inactive':
    label = 'Task inactive';
    tag = 'Inactive';
    variant = 'disabled';
    break;
  case 'ongoing':
    label = 'Ongoing task';
    tag = 'Ongoing';
    variant = 'primary';
    break;
}
Enter fullscreen mode Exit fullscreen mode

The lesson here is that the switch case starts to get bigger and more complex. To abstract this complexity, we can use object to map the status to an object that represents the status.

const statusMap = {
  finished: {
    label: 'Finished task',
    tag: 'Finished',
    variant: 'secondary',
  },
  inactive: {
    label: 'Task inactive',
    tag: 'Inactive',
    variant: 'disabled',
  },
  ongoing: {
    label: 'Ongoing task',
    tag: 'Ongoing',
    variant: 'primary',
  },
};

const { label, tag, variant } = statusMap['finished'];
label; // => Finished tag
tag; // => Finished
variant; // => secondary
Enter fullscreen mode Exit fullscreen mode

And if you are using a type system like TypeScript, we can do even better things.

We can type the statusMap’s key and value and require to use the existing keys.

type Statuses = 'finished' | 'inactive' | 'ongoing';
type StatusObject = {
    label: string;
    tag: string;
    variant: string;
};

type StatusMap = Record<Statuses, StatusObject>;
Enter fullscreen mode Exit fullscreen mode

And we used in the map:

const statusMap: StatusMap = {
    finished: {
        label: 'Finished task',
        tag: 'Finished',
        variant: 'secondary'
    },
    inactive: {
        label: 'Task inactive',
        tag: 'Inactive',
        variant: 'disabled'
    },
    ongoing: {
        label: 'Ongoing task',
        tag: 'Ongoing',
        variant: 'primary'
    },
};
Enter fullscreen mode Exit fullscreen mode

When you use it (and if your editor is configured to make the IntelliSense works), it will show all the possibilities for you.

Screen Shot 2021-12-28 at 10.45.07.png

It will also get errors in compile-time if you use a different key to access the object.

Screen Shot 2021-12-28 at 10.37.06.png

Great! Now we have a solution abstracting the complexity and getting errors in compile-time. In the future, it will also be possible to use pattern matching in JavaScript and we can come up with more solutions to handle conditions.

Resources

Top comments (0)