What are some strategies for encouraging tools to embrace TypeScript's exhaustiveness checking with minimal reliance on comments?

My coding style involves incorporating exhaustiveness checking in a lot of the code I write.

In the following code snippet, I intentionally introduce a type error in the line with _exhaustivenessCheck if there are additions made to the UnitOfTime union. While I appreciate this approach to programming, it becomes cumbersome when combined with code coverage tools and strict linters:

function calculateMilliseconds(unitOfTime: UnitOfTime): number {
  switch (unitOfTime) {
    case 'days':
      return number * ONE_DAY;
    case 'weeks':
      return number * ONE_WEEK;
  }
  /* istanbul ignore next */
  // eslint-disable-next-line
  const _exhaustivenessCheck: never = unitOfTime;
}

It can be frustrating needing to constantly inform both Istanbul and ESLint to disregard certain sections of code while carrying out good practices.

Is there an established best practice for addressing this issue without continually having to insert "ignore" or "disable" comments, or resorting to disabling lint rules and code coverage altogether?

Answer №1

When working with TypeScript from version TS 3.1.6 onwards (the oldest version I tested), there is no requirement to include an explicit exhaustiveness check like:

const _exhaustivenessCheck: never = unitOfTime;

In situations where the compiler recognizes final switch statements as exhaustive within a function. Therefore, an error will not be generated in scenarios such as this:

function waterFrequencyToMilliseconds(number: number, unitOfTime: UnitOfTime): number {
  switch (unitOfTime) {
    case 'days': {
      return number * ONE_DAY;
    }
    case 'weeks': {
      return number * ONE_WEEK;
    }
  }
}

However, if your switch statement lacks exhaustiveness, an error will occur:

function oopsWaterFrequencyToMillis(number: number, unitOfTime: UnitOfTime): number { 
// error! function lacks ending return statement --------------------------> ~~~~~~
  switch (unitOfTime) {
    case 'days': {
      return number * ONE_DAY;
    }
  }
}

Hence, for the specific code example provided above, the exhaustiveness check can be omitted.


Starting from TypeScript 3.7, the language offers improved support for detecting unreachable code through control-flow analysis. This eliminates the need to explicitly throw or return within sections of code identified by the compiler as unreachable, such as after an exhaustive switch statement. For more details, refer to this comment on the implementation's pull request.

Moreover, not only is an explicit exhaustiveness check unnecessary, but it will trigger an unreachability warning in TypeScript 3.7+:

function explicitExhaustiveCheck(number: number, unitOfTime: UnitOfTime): number {
  switch (unitOfTime) {
    case 'days': {
      return number * ONE_DAY;
    }
    case 'weeks': {
      return number * ONE_WEEK;
    }
  }
  const _exhaustivenessCheck: never = unitOfTime; // error!
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  //unreachable code detected.
}

Hence, it is advisable to remove such checks.


If you require compatibility with older TypeScript versions and possess sample code illustrating the necessity of an explicit exhaustiveness check, consider replacing the variable declaration with a specific return statement like return assertNever(unitOfTime), wherein assertNever() solely accepts arguments of type never. This adjustment may satisfy linting requirements. However, without a reproducible instance of the issue mentioned, the answer provided addresses the query comprehensively.

Access the code on TypeScript Playground

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

What is the process for importing libraries from a different local directory?

What I mean by that title is: I have some code that was generated and now I am incorporating it into my Angular application. Currently, I am installing this code as a package using npm, but it is causing issues with my deployment setup. So, I would like ...

How do I inform Jest that spaces should be recognized as spaces?

Here is some code snippet for you to ponder: import { getLocale } from './locale'; export const euro = (priceData: number): string => { const priceFormatter = new Intl.NumberFormat(getLocale(), { style: 'currency', currenc ...

Do you think it's wise to utilize React.Context for injecting UI components?

I have a plan to create my own specialized react component library. These components will mainly focus on implementing specific logic rather than being full-fledged UI components. One key requirement is that users should have the flexibility to define a se ...

retrieve the initial subarray from the array using rxjs

Looking to extract the first array from a subarray, my current setup is as follows: Map: map; Map() { Service }); } This is the interface structure: export interface map { } Encountering an error message: ERROR TypeError: ...

Encountering a hitch in loading Typescript modules

I'm attempting to utilize Typescript modules, but I'm encountering issues with loading them properly. Each time I try to import the module, I receive the following error message in my JS file: JavaScript runtime error: 'exports' is und ...

`Express routes in TypeScript`

Recently, I have been following a tutorial on how to build a Node.js app with TypeScript. As part of the tutorial, I attempted to organize my routes by creating a separate route folder and a test.ts file containing the following code: import {Router} fro ...

Passing specific props to child components based on their type in a React application using TypeScript

Sorry if this question has already been addressed somewhere else, but I couldn't seem to find a solution. I'm looking for a way to pass props conditionally to children components based on their type (i.e. component type). For example, consider ...

Extract keys from a list of interface keys to create a sub-list based on the type of value

Issue Can the keys that map to a specified value type be extracted from a TypeScript interface treated as a map? For example, consider the WindowEventMap in lib.dom.d.ts... interface WindowEventMap extends GlobalEventHandlersEventMap, WindowEventHan ...

Reactjs Promise left hanging in limbo

How can I resolve the pending status of my promise? I have a modal with a form submit in it, where I am trying to retrieve the base64 string of a CSV file. While my code seems to be returning the desired result, it remains stuck in a pending state. c ...

TypeScript failing to correctly deduce the interface from the property

Dealing with TypeScript, I constantly encounter the same "challenge" where I have a list of objects and each object has different properties based on its type. For instance: const widgets = [ {type: 'chart', chartType: 'line'}, {typ ...

When attempting to register a custom Gamepad class using GamepadEvent, the conversion of the value to 'Gamepad' has failed

I have been working on developing a virtual controller in the form of a Gamepad class and registering it. Currently, my implementation is essentially a duplicate of the existing Gamepad class: class CustomController { readonly axes: ReadonlyArray<nu ...

How can we update a boolean value in an Angular service using a set function?

Hey there! I'm currently working on updating a boolean value in my service when a button is clicked within my component. My goal is to trigger the setfunction and toggle the boolean value from true to false, and vice versa when the button is clicked a ...

I encountered an issue with my TypeScript function in Angular, as it is unable to process multiple uploaded files

I'm having trouble with my TypeScript function in Angular that is unable to read multiple uploaded files. fileUpload(event: Event) { const self = this; this.imageUploadInp = event.target as HTMLInputElement; this.imageUploadInp.addEventLis ...

Refine the search results by focusing on a specific property value

Assume I have a type defined as follows: type Result = {error:true,response: undefined} | {error:undefined, response:{nick:string}} This type will either contain an error property or a response, but not both. Now, I am attempting to create a function tha ...

At a specific duration of inactivity on the website, I am automatically redirected to the login page due to session expiration

When I navigate through the URL based on the session, the session will expire if the user is inactive on the site. The issue arises when a user is considered inactive because the session expires after a certain period of inactivity and then redirects to th ...

Can someone explain how to create a Function type in Typescript that enforces specific parameters?

Encountering an issue with combineReducers not being strict enough raises uncertainty about how to approach it: interface Action { type: any; } type Reducer<S> = (state: S, action: Action) => S; const reducer: Reducer<string> = (state: ...

"Exploring the advancements in inner calls and the deprecation of forkJoin

Here is the code snippet I am currently working with: ngOnInit(): void { this.issueService.getIssues().pipe( switchMap(issues => { this.issuesList = issues; const observables = this.issuesList.map(issue => this.issueService.getChild ...

What is the process for adjusting the color of axes in vue-chartjs?

Seeking help on how to adjust the color of the axis in my graph. Has anyone encountered a similar issue before? The chart I'm working with resembles the one shown in the image. Not sure if this issue is related to it being a time graph. Below is the V ...

How can I move the cursor to the beginning of a long string in Mat-autocomplete when it appears at the end?

I'm struggling to figure out how to execute a code snippet for my Angular app on Stack Overflow. Specifically, I am using mat-autocomplete. When I select a name that is 128 characters long, the cursor appears at the end of the selected string instead ...

How to generate an interactive text-box using Angular 8 and connecting the input to the component during form submission

When I attempt to add a dynamic text box after clicking a button, the text box is successfully added. However, I am facing an issue where I am unable to retrieve all the values (values of dynamically added text boxes) when the form is submitted. It seems t ...