2 Comments

Don’t Replace Your Switch Statements with Object Literals

Because I work on a large full-stack JavaScript project, I see a lot of styles of JavaScript code. Some functional, some procedural, some on odd mix of the two. But one of the things that I’ve come to see as a red flag is the switch statement.

I imagine it always starts out with good intentions: you have a number of different cases that require different handling, so you make a switch statement! Depending on the case, certain code gets executed. It sounds really readable and maintainable, right? Well, it rarely works out that way.

An Intriguing Idea

A while back, I read this really clever blog post by Angular guru Todd Motto. “Replace your switch statements with object literals,” he says. And that sounds great! Just use a lookup table for the code you want to execute. You can even mimic a default case by cleverly OR-ing your lookups before attaching parentheses for a function call*. If you’re using ES6, you can even use a constants object to provide keys for each function via computed property keys.

So for instance, here’s a switch statement.

var area;
switch(shape.name) {
    case Shapes.TRIANGLE:
        area = .5 * shape.base * shape.height;
        break;
    case Shapes.SQUARE:
        area = shape.height * shape.height;
        break;
    case Shapes.RECTANGLE:
        area = shape.height * shape.length;
        break;
    case Shapes.CIRCLE:
        area = 3.14 * shape.radius * shape.radius;
        break;
    default:
        throw new Error('shape not recognized');
}

And here’s its equivalent object literal with an invocation simulating fall-through behavior.

var areaCalculator = {
    [Shapes.TRIANGLE]: (shape) => .5 * shape.base * shape.height,
    [Shapes.SQUARE]: (shape) => shape.height * shape.height,
    [Shapes.RECTANGLE]: (shape) => shape.height * shape.width,
    [Shapes.CIRCLE]: (shape) => 3.14 * shape.radius * shape.radius,
    default: () => {throw new Error('shape not recognized');}
};

Anyway. I saw this clever post, and I thought to myself: I should totally do this. By using lookup tables instead of switches, my code will be more maintainable—and stuff! I waited, and I never came across an opportunity to write a switch statement or its object literal counterpart.

Better Understanding the Humble Switch Statement

Finally, in a code review, I saw a switch that another developer wrote, only it was a terrible candidate for replacement with an object literal. Why? Because this developer writes really good code. Each code block was simply returning a function written (and tested) for each case. There was definitely no point in contorting it into an object full of functions.

That’s when I realized: I only hated them when they were written in a procedural style (I have a hunch they’re more common in procedural-style code). If you practice data immutability and write small, well-composed functions, then if you ever happen to need to write a switch statement, it will be readable. It likely won’t even need its own test, since the function for each case will already be tested!** It’s the procedural code that uses fall through and data mutation to achieve its ends that was the problem, not the humble switch statement.

I imagine that if you’re writing a lot of procedural code, then Todd Motto’s advice on using object lookups is pretty helpful. There’s no opportunity to forget a break, flow of control is obvious to any reader, and future developers don’t have to worry about where they ought to add a case without running afoul of a past developer’s intended fall-through behavior. But if you practice a functional style, there’s little reason to avoid a lightweight switch statement.

And besides, the code sample I presented above is a little bit bogus. If you return a value for each case instead of assigning a value to a variable, then you’ve got one-liners that are easy to read.

A Case for Functional Style

So, the moral of the story: use a functional style and a lot of your problems aren’t problems anymore. Or adopt very clever language hacks in order to circumvent awkward control structures. You pick!

*it’s questionable why you’d ever want to do this other than to exactly replace a switch statement, which is odd since the whole idea is to avoid futzing with the weird flow of control of a switch in the first place.

**yes, you could unit test it, but it would likely be an extensively mocked test with very little real value, and its behavior ought to be caught by an integration test anyway