Need help understanding a JavaScript switch statement

I’m struggling to get a JavaScript switch statement to behave the way I expect. Some cases don’t seem to run, and the default block fires even when a matching value should exist. I’m not sure if I’m missing break statements, comparing the wrong types, or structuring my logic incorrectly. Can someone explain how switch statements really work in JavaScript and show a simple, correct example so I can compare it to my code?

Most switch bugs in JS come from a few common things. You can check these one by one.

  1. Type mismatch
    switch uses strict equality (===).
    So this fails:
    let x = ‘1’;
    switch (x) {
    case 1:
    console.log(‘number 1’);
    break;
    default:
    console.log(‘default’);
    }
    You get ‘default’.
    Make sure the types match. Log typeof value before the switch:
    console.log(value, typeof value);

  2. Missing break
    If you forget break, execution falls through to the next case and even to default:
    switch (x) {
    case 1:
    console.log(‘one’);
    // missing break
    case 2:
    console.log(‘two’);
    break;
    default:
    console.log(‘default’);
    }
    For x === 1, you get ‘one’, ‘two’, maybe ‘default’ if you miss the break at 2 too.
    Double check every case and the default. Put break or return at the end of each block:
    case 1:
    doSomething();
    break;

  3. Wrong value in switch expression
    Log what you switch on:
    console.log(‘switching on:’, myVar);
    switch (myVar) {

    }
    Many times the bug is myVar is undefined or not what you think.

  4. Extra spaces or different strings
    String cases must match exactly:
    switch (status) {
    case ‘OPEN’:

    break;
    case ‘CLOSED’:

    break;
    }
    If status is ‘open’ or 'OPEN ’ you hit default.
    Trim and normalize:
    const statusNorm = String(status).trim().toLowerCase();
    switch (statusNorm) {
    case ‘open’:

    break;
    }

  5. Using expressions in case wrongly
    This is wrong:
    switch (x) {
    case x > 10:

    }
    That compares x to true or false.
    For ranges, use if/else:
    if (x > 10 && x < 20) { … }

  6. Default in the middle and fallthrough
    This works but confuses people:
    switch (x) {
    case 1:

    default:

    break;
    case 2:

    }
    If you forget break above default, you hit default when you do not expect.
    Keep default last while you debug.

Quick checklist for your code:
• Log the value you switch on and its type.
• Make sure all case values match type and content.
• Add break or return to every case and to default.
• Avoid boolean expressions inside case.

If you post the exact snippet, people can point at the exact failing line in about 10 seconds.

Couple more angles to check that haven’t been covered by @chasseurdetoiles:

  1. switch(true) pattern vs plain switch(value)
    Sometimes people mix these up:

    switch (value) {
      case value > 10:
        // this NEVER does what you think
    }
    

    If you actually want range checks with switch, it must be:

    switch (true) {
      case value > 10 && value <= 20:
        // ...
        break;
      case value > 20:
        // ...
        break;
      default:
        // ...
    }
    

    If your code mixes switch(value) with boolean case expressions, you’ll hit default all the time and think JS is broken.

  2. Hidden coercion before the switch
    Even though switch uses ===, your value might already be coerced earlier:

    let status = someObj.status || '0';   // now it is always a string
    
    switch (status) {
      case 0:
        // never hit, because status is `'0'`
        break;
    }
    

    The fix is either change the case to '0' or normalize the value explicitly:

    const statusNum = Number(status);
    switch (statusNum) {
    

    I’d actually say “log typeof” as suggested is useful, but I disagree slightly: don’t just log it, assert it:

    if (typeof value !== 'string') {
      throw new Error('Expected string in switch, got ' + typeof value);
    }
    

    That makes bugs scream instead of silently falling into default.

  3. Asynchronous or changing value
    If your switch is inside an async callback or event handler, sometimes the variable you switch on has changed by the time it runs:

    let code = 1;
    
    setTimeout(() => {
      switch (code) {
        case 1:
          // you *think* this runs
          break;
        default:
          // but maybe `code` was reassigned before timeout fires
      }
    }, 0);
    
    code = 3;
    

    Snapshot the value:

    const current = code;
    switch (current) { ... }
    
  4. Object / array cases
    If you’re doing something like:

    const val = { type: 'A' };
    
    switch (val) {
      case { type: 'A' }:
        // never matches
        break;
    }
    

    That will always fall to default because objects compare by reference, not structure. Same for arrays. Extract a primitive:

    switch (val.type) {
      case 'A':
        // works
        break;
    }
    
  5. Minimal repro as a sanity check
    Take your broken switch and paste it into something like:

    const x = /* hardcode the value you *expect* to match */;
    console.log('x:', x, typeof x);
    
    switch (x) {
      case 'something':
        console.log('hit something');
        break;
      case 123:
        console.log('hit 123');
        break;
      default:
        console.log('hit default');
    }
    

    If it works here but not in your real code, the issue is not the switch syntax at all, it is the data flow into it. In practice that ends up being “this variable isn’t what I think it is” about 95% of the time.

If you can post the exact snippet with 1 example input that should match but hits default, folks can usually point at the exact line that’s trolling you.