Switch for specific type in TypeScript

You'd be better off using an if statement with typeguards.

let action: Action = ...;
if (isSpecificAction(action)) {
    console.log(action.payload);
}

function isSpecificAction(action: any): action is SpecificAction {
    return action.payload;
}

At the end of the day, TypeScript is still JavaScripty, and the switch statement gets transpiled to a regular JS switch:

A switch statement first evaluates its expression. It then looks for the first case clause whose expression evaluates to the same value as the result of the input expression (using the strict comparison, ===)

So in your case:

interface Action {}
class SpecificAction implements Action {
   payload?: any
}

let action: Action
switch (action) {
   case SpecificAction: //it works
       console.log(action.payload) // it doesn't 
}

action would be evaluated and compared with the class SpecificAction. Presumably, action is an instance of SpecificAction (or some object that implements the Action interface).

With a switch, you could do this:

let a: Action = new SpecificAction();
let b: Action = a;

switch (a) {
    case b:
        console.log("Worked");
}

The expression a is evaluated and compared to the expression b (and a === b, so we hit the console.log), but that's obviously not what you're looking for.

If you want to check if an instance is of a particular type (re: class), then you should use a type guard. A switch/case is the wrong construct.


Alternatively, why not use instanceof?

interface Action { };
class SpecificAction implements Action {}
class NotSpecificAction implements Action {}

let action: Action = new SpecificAction();
console.log(action instanceof SpecificAction); // true
console.log(action instanceof NotSpecificAction); // false

for the time being it looks like there are a few options, all of them with some drawbacks

  • discriminated unions docs stackblitz, but you'll need a dedicated property as discriminator
interface Action {}

class SpecificAction implements Action {
  kind: "specific";
  payload?: any;
}

class ToggleAction implements Action {
  kind: "toggle";
  toggle: boolean;
}

let action: SpecificAction | ToggleAction;
switch (action.kind) {
  case "specific":
    console.log(action.payload) // it works 
    break;
  case "toggle":
    console.log(action.toggle) // it works 
    break;        
}
  • User-Defined Type Guards docs stackblitz, but you'll need if statements instead of switch
interface Action {}

class SpecificAction implements Action {
  payload?: any;
}

class ToggleAction implements Action {
  toggle: boolean;
}

let isSpecific = (p: any): p is SpecificAction => !!p.payload
let isToggle = (p: any): p is ToggleAction => !!p.toggle

let action: Action;
if (isSpecific(action)) {
  console.log(action.payload) // it works 
} else if (isToggle(action)) {
  console.log(action.toggle) // it works 
}
  • constructor property github stackblitz, but you'll need to cast to desired type for the time being
interface Action { }

class SpecificAction implements Action {
  payload?: any;
}

class ToggleAction implements Action {
  toggle: boolean;
}

switch (action.constructor) {
  case SpecificAction:
    console.log((<SpecificAction>action).payload) // it kinda works 
    break;
  case ToggleAction:
    console.log((<ToggleAction>action).toggle) // it kinda works 
    break;
  }

Tags:

Typescript