Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure switch/case includes all enums with TypeScript?

I have 2 enums Color and Shape. The SHAPES_BY_COLOR constant maps the available shapes to the individual colors.

Later, I want to perform different actions based on the selected color and shape by using a simple switch/case statement.

Is there a way that TypeScript "tells me" that a shape is missing or not available for the current color?

enum Color {
  blue = "blue",
  green = "green",
  red = "red",
  orange = "orange"
}

enum Shape {
  star = "star",
  rectangle = "rectangle",
  ellipse = "ellipse",
  triangle = "triangle",
  diamond = "diamond",
}

const SHAPES_BY_COLOR: Record<Color, Shape[]> = {
  [Color.blue]: [
    Shape.triangle,
    Shape.diamond,
  ],
  [Color.green]: [
    Shape.star,
    Shape.triangle,
    Shape.diamond,
  ],
  [Color.red]: [
    Shape.ellipse,
    Shape.triangle,
  ],
  [Color.orange]: [
    Shape.star,
    Shape.triangle,
    Shape.diamond,
  ]
}

const getResultForColorBlue = (shape: Shape) => {
  // QUESTION: How to ensure that all cases from color "blue" are included here?
  // Get a subset of the `Shape` enum from SHAPES_BY_COLOR[Color.Blue]?
  switch (shape) {
    case Shape.triangle:
      return "";
    // Show TS error -> Missing shape "Shape.diamond"
    case Shape.star: // Show TS error -> Option not available for Color.blue
        return "";
  }
};

like image 755
Nepo Znat Avatar asked Nov 29 '25 13:11

Nepo Znat


2 Answers

For this case, I use never as a trick. When all your options are exhausted the shape will become never otherwise it will be one of the shape.

enum Color {
    blue,
    green,
    red,
    orange
}

enum Shape {
    triangle,
    star,
    diamond,
    ellipse
}

const SHAPES_BY_COLOR: Record<Color, Shape[]> = {
  [Color.blue]: [
    Shape.triangle,
    Shape.diamond,
  ],
  [Color.green]: [
    Shape.star,
    Shape.triangle,
    Shape.diamond,
  ],
  [Color.red]: [
    Shape.ellipse,
    Shape.triangle,
  ],
  [Color.orange]: [
    Shape.star,
    Shape.triangle,
    Shape.diamond,
  ]
}

const getResultForColorBlue = (shape: Shape) => {
  // QUESTION: How to ensure that all cases from color "blue" are included here?
  // Get a subset of the `Shape` enum from SHAPES_BY_COLOR[Color.Blue]?
  switch (shape) {
    case Shape.triangle:
      return "";
    case Shape.star: // Show TS error -> Option not available for Color.blue
        return "";
    case Shape.ellipse: // Show TS error -> Option not available for Color.blue
        return "";
    case Shape.diamond: // Show TS error -> Option not available for Color.blue
        return "";
     default:
        // if any shape is not handled shape won't be never and thus you will get compile time error
        const exhaustiveCheck: never = shape 
        console.log("will trhow type (compile time) error if any category is not handled by switch case")
        return ""

  }
};
like image 152
Rahul Pol Avatar answered Dec 01 '25 05:12

Rahul Pol


You can do that with ESLint plugin @typescript-eslint/eslint-plugin and using this rule switch-exhaustiveness-check

From your example, you will get this error message

error  Switch is not exhaustive. Cases not matched: Shape.rectangle | Shape.ellipse | Shape.diamond  @typescript-eslint/switch-exhaustiveness-check
like image 29
foray1010 Avatar answered Dec 01 '25 07:12

foray1010