Consider 3 Scenario
Scenario 1: Boolean condition
if (condition) {}
else {}
Specifying a condition as else if would be redundant, and it's really obvious to the reader what the code does. There is no argument for using else if in this case.
Scenario 2: Infinite states
Here we are interested in testing for conditions A and B (and so on), and we may or may not be interested in what happens if none of them holds:
if (conditionA) {}
else if (conditionB) {}
else {} // this might be missing as it is in your case
The important point here is that there isn't a finite number of mutually-exclusive states, for example: conditionA might be num % 2 == 0
and conditionB might be num % 3 == 0
.
I think it's natural and desirable to use a reasonable amount of branches here; if the branches become too many this might be an indication that some judicious use of OO design would result in great maintainability improvements.
Scenario 3: Finite states
This is the middle ground between the first two cases: the number of states is finite but more than two. Testing for the values of an enum-like type is the archetypal example:
if (var == CONSTANT_FOO) {}
else if (var == CONSTANT_BAR) {} // either this,
else {} // or this might be missing
In such cases using a switch is probably better because it immediately communicates to the reader that the number of states is finite and gives a strong hint as to where a list of all possible states might be found (in this example, constants starting with CONSTANT_). My personal criteria is the number of states I 'm testing against: if it's only one (no else if) I 'll use an if; otherwise, a switch. In any case, I won't write an else if in this scenario.
Adding else as an empty catch-errors block
This is directly related to scenario #2 above. Unless the possible states are finite and known at compile time, you can't say that "in any other case" means that an error occurred. Seeing as in scenario #2 a switch would feel more natural, I feel that using else this way has a bad code smell.
Use a switch with a default branch instead. It will communicate your intent much more clearly:
switch(direction) {
case 'up': break;
case 'down': break;
default: // put error handling here if you want
}
This might be a bit more verbose, but it's clear to the reader how the code is expected to function. In my opinion, an empty else block would look unnatural and puzzling here.