Ensuring full enum coverage in TypeScript switch/case statements

I have 2 enums Color and Shape. The constant SHAPES_BY_COLOR connects shapes with different colors.

In the future, I aim to execute specific actions depending on the selected color and shape using a switch/case statement.

Is there a way for TypeScript to notify me if a particular shape is missing or unavailable 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 do we make sure all cases from color "blue" are covered here?
  // Can we 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 "";
  }
};

Answer №1

When faced with no other options, I resort to using the 'never' trick. The shape will turn into 'never' if all possibilities are exhausted; otherwise, it will fall under one of the shapes.

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 can we ensure that all cases for color "blue" are covered here?
  // Retrieve a subset of the `Shape` enum from SHAPES_BY_COLOR[Color.Blue]?
  switch (shape) {
    case Shape.triangle:
      return "";
    case Shape.star: // Display TS error -> Option not available for Color.blue
        return "";
    case Shape.ellipse: // Display TS error -> Option not available for Color.blue
        return "";
    case Shape.diamond: // Display TS error -> Option not available for Color.blue
        return "";
     default:
        // If any shape is unhandled, shape won't be 'never', resulting in a compile-time error
        const exhaustiveCheck: never = shape 
        console.log("Will throw a type (compile time) error if any category is not handled by the switch case")
        return ""

  }
};

Answer №2

To achieve this, utilize the ESLint plugin known as @typescript-eslint/eslint-plugin and apply the rule switch-exhaustiveness-check

When looking at your scenario, you will encounter the following error message

error  Switch is not exhaustive. Cases not matched: Shape.rectangle | Shape.ellipse | Shape.diamond  @typescript-eslint/switch-exhaustiveness-check

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Preventing Cross-Site Scripting (XSS) when injecting data into a div

I am using Ajax to fetch data in JSON format and then parsing it into a template. Here is an example of the function: function noteTemplate(obj) { var date = obj.created_at.split(/[- :]/), dateTime = date[2] + '.' + date[1] + '. ...

Is the process of adding a new row by duplicating an already existing row dynamic?

I've encountered an issue where a row in a table is not being displayed because the CSS style has been set to display:none. Here's the HTML table: <table class="myTable"> <tr class="prototype"> ........ </tr> </ ...

I'm curious about the origin and purpose of req.user - where does it come from and

Recently, I've been delving into Nestjs and found myself a bit puzzled by the req.user. Where does this come from and do we have to manually request it? What exactly is req.user and what advantages does it offer? Must I assign payload to it manually? ...

The height appears differently on Mozilla compared to the Chrome browser

I currently have three sections on my website: "Header" with a height of 100px. "Content Area" with a height of 462px. "Footer" with a height of 100px. The strange issue I am facing is that in Chrome, the scroll bar is invisible (which is ideal), but in ...

When I refresh the page in Angular2, the router parameters do not get loaded again

When loading my application on routers without parameters, everything is normal. However, when trying to use a router with params, the application fails to load. For example: localhost:3000/usersid/:id The code for the router is as follows: const appRou ...

What is the best way to reduce the file size of a group of PNG images for

For my game character, I have a collection of PNG files including stand.png, run1.png, run2.png. To display a series of actions on the JavaScript canvas, I need to load these images. However, the size of each .png file adds up quickly – for example, it ...

Using jQuery to detect changes in the value of form fields, excluding any changes related to display toggles

Currently, I am in the process of revamping a large form that includes various input fields and dropdown menus. The goal is to submit it via AJAX once a field loses focus but only if the value has been altered. To achieve this, I have set up a jQuery selec ...

Error 8007 encountered when attempting to scale at 100% proficiency. Transformation unsuccessful

Wondering if this could be a bug in Photoshop. When scaling a layer and entering values of 100%, an error message pops up: var srcDoc = app.activeDocument; var numOfLayers = srcDoc.layers.length; // main loop for (var i = numOfLayers -1; i >= 0 ; i-- ...

Issue: A React component went into suspension during the rendering process, however, no alternative UI was designated

I'm currently experimenting with the code found at https://github.com/adarshpastakia/ant-extensions/tree/master/modules/searchbar Although I followed the tutorial instructions, I encountered an error. Could it be that the library is malfunctioning? I ...

PHP trigger update query to modify table data on click event

I specialize in creating system notifications and have encountered an issue. My goal is to update the 'notifications' table from 'new=1' to 'new=0' for a logged-in user after a click event. How can I achieve this database up ...

Manipulating div positions using JQuery and CSS

Whenever I hover over the #HoverMe div, a hidden #hidden div appears, and when I unhover it, the hidden div disappears again. The #hidden div contains a sub-div that functions like a dropdown list. The #hidden div is styled with position: absolute;. How ca ...

JavaScript drag functionality is jerky on iPads, not seamless

I am currently attempting to implement a feature where I can drag a div (#drag) within its parent container (#container) using only pure JavaScript. This functionality is specifically required to work on iPad devices only. After writing a script that func ...

Can a variable be used to search for a particular element within a JSON script?

Here's an example script that I need to modify to log the name Joe: const names = ` { "Joe": { "nat": "American", "hair": "brown" }, "Pet ...

delay in displaying options when toggling visibility in a dropdown menu

When you first click on the select, it displays incorrectly https://i.sstatic.net/Ax9T7j8J.png But when you click on it a second time, it displays correctly https://i.sstatic.net/UpW4krED.png $(document).on("click", "#edit_afpDetalle_mes&q ...

Typescript struggles to identify properties that have no business being there

In my function for formatting data, the "values" contained within this data are clearly defined. However, TypeScript is failing to recognize new properties that are invalid when mapping these values. The issue can be best understood by looking at the code ...

AngularJS issue: Form submission does not trigger the controller function

I am currently learning AngularJS and I am experimenting with the sample code below to send my form data to the server. <!doctype html> <html lang=''> <head> <meta charset="utf-8"> <script src="../js/angular.min.v1 ...

Is it possible to implement pagination on a material table in Angular 10 without relying on the MatTableDataSource component?

How can I implement material pagination on a table without using MatTableDataSource? Most tutorials and examples I find online recommend the use of MatTableDataSource, but I'm unsure of how to actually utilize it. I am fetching data from a database ta ...

What causes the function endpoint to become unreachable when a throw is used?

One practical application of the never type in typescript occurs when a function has an endpoint that is never reached. However, I'm unsure why the throw statement specifically results in this unreachable endpoint. function error(message: string): ne ...

Choosing the entire contents of a webpage using jQuery

Is there a way to use jQuery to select all the content on a webpage and copy it to the clipboard for use in another WYSIWYG editor? Here's the scenario: $("#SelectAll").click(function(){ //CODE TO SELECT ALL THE CONTENTS OF THE CURRENT PAGE /* PS: $ ...

Is there a different approach available since the array function "some" does not restrict type even when a type predicate is implemented?

It is expected that when using the array function "some" along with a type predicate and return statement, the TypeScript compiler would narrow down the type of dashArray. Is it reasonable to expect this behavior from the TypeScript compiler or am I incor ...