Leaving all former and historical discussion on"why not usingswitch
statements" behind, I'd like to show my approach on how to get the most out of it:
functionprocessByType({type,...args}){switch(type){caseCONST_VALUE_A:returnprocessArgsA(args)caseCONST_VALUE_B:returnprocessArgsB(args)caseCONST_VALUE_C:returnprocessArgsC(args)default:thrownewError(`unknown type${type}`)}}
Let me explain a bit, why I think this is a greatswitch
concept:
Single responsibility
I think we should approach theswitch
with 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 oftype
).
Derived from this assumption, we may conclude the following:
It has no operational code in branches
Let theswitch
defer processing to external functions, because it is not the responsibility of theswitch
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
Theswitch
can only be isolated when wrapped in a function. Also increased the reuse and testability.
It uses constants ascase
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 usebreak
statements
Thebreak
is something like the spaghetti-code's topping to me. It "breaks" control flow in an unnatural way, where it jumps like agoto
to an invisibly tagged point after theswitch
block.
However, since there is no need to execute code after theswitch
block we can safely return the result of the function called in thecase
branch.
It throws and error as default
Defining adefault
and not throwing an error is like writing anif-else
logic with multiple if-conditions - you can't control the reason why it branched into theelse
.
The same thing applies fordefault
. Thetype
value could by anything, ranging fromundefined
,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 uncoveredtype
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 andswitch
should just be banned from the language set, please let me know.
Top comments(2)

Nice pont of view. Another way that I do the same thing but not useswitch 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);}
For further actions, you may consider blocking this person and/orreporting abuse