How to write a great switch statement in JavaScript

jankapunkt profile image Jan Küster ・2 min read

Leaving all former and historical discussion on "why not using switch statements" behind, I'd like to show my approach on how to get the most out of it:

function processByType ({ type, ...args }) {
  switch (type) {
    case CONST_VALUE_A:
      return processArgsA(args)
    case CONST_VALUE_B:
      return processArgsB(args)
    case CONST_VALUE_C:
      return processArgsC(args)
      throw new Error(`unknown type ${type}`)

Let me explain a bit, why I think this is a great switch concept:

Single responsibility

I think we should approach the switchwith something similar to the single responsibility principle.
Basically, it's purpose is to decide which case branch to execute by a given value. The only reason to change the switch code is an updated logic on the value mapping (which case branch to execute, based on which value of type).

Derived from this assumption, we may conclude the following:

It has no operational code in branches

Let the switch defer processing to external functions, because it is not the responsibility of the switch to implement the concrete processing.

It should also contain no pre- or post-processing code but just pass through arguments from input to the defined functions.

It is wrapped in a function

The switch can only be isolated when wrapped in a function. Also increased the reuse and testability.

It uses constants as case match conditions

This is a tricky one and I am 50/50 on it. On the one hand using constants reduces the error rate of typos. On the other hand it makes the function dependent on some external definitions.

I think this depends on the case and the overall need for scalability. Anyway, I tend to favour constants from an aesthetic point of view.

It does not use break statements

The break is something like the spaghetti-code's topping to me. It "breaks" control flow in an unnatural way, where it jumps like a goto to an invisibly tagged point after the switch block.

However, since there is no need to execute code after the switch block we can safely return the result of the function called in the case branch.

It throws and error as default

Defining a default and not throwing an error is like writing an if-else logic with multiple if-conditions - you can't control the reason why it branched into the else.

The same thing applies for default. The type value could by anything, ranging from undefined, null or a totally different type.

To indicate such an undefined state it is therefore conclusive to throw an error here and examine the source of this uncovered type value.

What do you think?

Note, that this has derived from my personal experience, mixed with what I picked up from readings or observed from other projects (for example the usage of constants in branching logic is something I have learned when working with Java).

If you still think there is something flawed in this concept and switch should just be banned from the language set, please let me know.


Editor guide
leandroandrade profile image
Leandro Andrade

Nice pont of view. Another way that I do the same thing but not use switch is:

function processByType({ type, ...args }) {
    const fns = {
        CONST_VALUE_A: processArgsA,
        CONST_VALUE_B: processArgsB,
        CONST_VALUE_C: processArgsC

    const fn = fns[type];
    if(!fn) throw new Error(`unknown type ${type}`);
    return fn(args);
harryadel profile image
Harry Adel

Was just about to point the same thing. I feel switch statements are obsolete when you got bracket notation that allows you to do the same thing in a much simpler way.