Have you ever written a switch statement that checks all the possible cases in a JavaScript ENUM? It might look something like this:

enum Animals {
  Cat,
  Dog,
  Frog,
}

function getSound(animal: Animals) {
  switch (animal) {
    case Animals.Cat:
      return 'Meow!';
    case Animals.Dog:
      return 'Woof!';
    case Animals.Frog:
      return 'Ribbit!';
  }
}

getSound(Animals.Cat);

So, what happens if you add another animal like Donkey? In your compiled JavaScript, nothing will happen until the user comes across a scenario where the animal is a Donkey. ¬†ūüí•! Your app just crashed or at best didn't do what the user was expecting.

Fortunately, TypeScript is smart enough to catch this mistake for you.  If you add Donkey to the enum, TypeScript will try to warn you that you haven't checked for the case of Animal.Donkey.  Here's what that looks like:

In this scenario, TypeScript is literally telling you that you didn't account for all the possible animals.  This is just one example of how TypeScript can improve your code and keep your users from discovering your coding errors.

Now, that TypeScript warning certainly helps while you're editing the switch code.  However, if you just happen to add a new animal to the enum, you get no special warning right away.  This is a really big problem if the enum is defined in a separate file.  The odds of you catching the mistake in file(s) containing the getSound method are pretty slim.

To solve this problem, your code needs a pre-commit hook that checks for type errors. If you aren't going to do that, at the very least, you should manually run linting before committing or in your CI process.  You can configure an npm script to perform these checks like this:

"lint": "tsc --noEmit && eslint 'src/**/*.{js,jsx,ts,tsx}' --max-warnings 0"

Then, you'll get a lovely warning like this in your terminal:

src/components/form/test.tsx:18:10 - error TS7030: Not all code paths return a value.

18 function getSound(animal: Animals) {