In the else-branch, a type guard of "not null" results in resolving to "never."

After creating a type guard that checks for strict equality with null:

function isNotNull<T> (arg: T): arg is Exclude<T, null> {
  return arg !== null
}

Testing it showed that the then-branch successfully removes null from the type.

const value: string | null = 0 as any
if (isNotNull(value)) {
  // The type of `value` is now `string`
}

However, in the else-branch, it ends up being never.

const value: string | null = 0 as any
if (isNotNull(value)) {
  // Type of `value` remains `string` here
} else {
  // Type of `value` is now `never` here
}

The goal is to make it resolve back to null in the else-branch.

Is there a way to achieve this?

Answer №1

The issue at hand lies with the assignment:

const value: string | null = 0 as any

Since the compiler recognizes that value is a constant, it understands that it can never be null.

Even if you use let, the situation doesn't improve much:

let value: string | null = 'foo';
if (isNotNull(value)) {
  // `value` is of type `string` in this block
} else {
  // `value` is of type `never` in this block
}

Once again, TypeScript's static analysis knows that the value cannot be null.

However, if you assign a value where TypeScript cannot deduce a constant, then you will find your anticipated string in the if condition and null in the else:

function isNotNull<T> (arg: T): arg is Exclude<T, null> {
  return arg !== null
}

let value: string | null = foo();

if (isNotNull(value)) {
  // `value` is of type `string` here
} else {
  // `value` is of type `null` here
}

function foo(): string | null {
    return "foo"
}

Even in this scenario, it only functions when the strictNullChecks compilation setting is enabled. Without strictNullChecks, you cannot eliminate null from the type, and the if branch defaults to being just string which aligns with string | null, hence resulting in never for the else section.

Edit: The reason behind this not functioning without strictNullChecks is intriguing. In such instances, the types string | null and string are considered identical. Consequently, value fundamentally possesses the type string, leading Exclude<string, null> to essentially represent string (thus still encompassing null), leaving the else portion with type never for value.

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

Developing personalized middleware definition in TypeScript for Express

I have been struggling to define custom middleware for our application. I am using [email protected] and [email protected]. Most examples of typing middleware do not involve adding anything to the req or res arguments, but in our case, we need to modify ...

Vue/Vite vanilla setup encountering a 'Failed to fetch dynamically imported module' TypeError

We're currently working with a vanilla Vue/Vite setup and I'm encountering the error message TypeError: Failed to fetch dynamically imported module in Sentry logs. It appears that these errors coincide with new deployments to production, but I d ...

Is there a way to combine multiple array objects by comparing just one distinct element?

Is there a way to combine these two arrays into one? array1 = [ { image: 'image1', title: 'title1' }, { image: 'image2', title: 'title2' }, { image: 'image3', title: 'title3' }, ]; array2 = ...

Angular-Slickgrid: Some filters may not display on the header row

After thorough investigation and experimentation, I was able to identify both the issue and a suitable solution: The problem stemmed from our dataset's usage of numeric IDs (e.g. 1,2,3,...). Within the code, there was an error where the grid misinter ...

Ways to utilize the scan operator for tallying emitted values from a null observable

I'm looking for an observable that will emit a count of how many times void values are emitted. const subject = new Subject<void>(); subject.pipe( scan((acc, curr) => acc + 1, 0) ).subscribe(count => console.log(count)); subject ...

Refresh Material-Ui's Selection Options

Is there a way to properly re-render the <option> </option> inside a Material UI select component? My goal is to transfer data from one object array to another using the Material UI select feature. {transferData.map(data => ( <option ...

Easy steps to bring in type definitions from an npm package using Vite

I am currently developing a package with several ts functions that will be utilized by multiple repositories, including mobile and web applications. In our team, we use vite as our primary build tool, which is also integrated into the repository. Below is ...

When using Angular 2, the `onYouTubeIframeAPIReady` function may not be able to locate the `YT` name, even if it is defined on the

As part of my efforts to track when a user pauses a YouTube video (link to the question), I have been utilizing the onYouTubeIframeAPIReady callback method in Angular2. I am facing similar challenges as discussed here and here. Following the recommendati ...

Implementing the handling of multiple button events in a ListView through onclick function

Currently, I have a listview with three buttons that need to trigger the same method checkInstall on multiple button clicks. However, I am unsure of how to achieve this. Below is the relevant code snippet: html file: <ListView [items]="allAppsList" c ...

The functionality of the Ionic menu button becomes disabled once the user has successfully logged in

Having trouble clicking the button after taking a test. Situation: Once logged in -> user takes a test and submits -> redirected to home page. However, unable to click on "Menu button" on the home page. In my Login.ts file: if (this.checker == " ...

Determine if an element in Angular 6 contains a particular style

Below is a div, and the first time you click on it, an opacity style is added. I am looking to determine within the same function if this div has an opacity style set to 1. @ViewChild('address') private address: ElementRef; public onClickAddres ...

Can an Angular 2 module export an interface?

While attempting to export an interface in a NgModule-declaration, I encountered an error message in my editor (Visual Studio Code) stating: [ts] 'MyInterface' only refers to a type, but is being used as a value here. Below is the code snippet c ...

Angular index.html file can include a conditional script

I am currently working on an Angular project, where the index.html serves as the main entry point for the application, just like in any other Angular project. This file contains important links and configurations. Within the HTML code snippet below, you w ...

TypeScript encountered an error with code TS2305, stating that the module "constants" does not have any exported members

My Vite + React + TypeScript application has the following structure: src constants a.ts b.ts index.ts components Comp.tsx tsconfig file with "baseUrl": "src" The content of a.ts is as follows: export const ARRAY = ...

Ways to resolve issues related to null type checking in TypeScript

I am encountering an issue with a property that can be null in my code. Even though I check for the value not being null and being an array before adding a new value to it, the type checker still considers the value as potentially null. Can anyone shed lig ...

Error in TypeScript: Circular reference in type alias 'Argument' in node_modules/classnames/index.d.ts at line 13:13

Need help troubleshooting an error while building my project. Can't find much information online to resolve this issue. I am using typescript 4.2.4 I can't locate the classnames/index.d.ts file in any directory, so I'm unable to make any ch ...

Parameters in Typescript decorators

Can someone help me understand the various parameters of a Typescript decorator? function myDecorator(target) { // do something with 'target' ... } In the given example, I am aware that 'target' represents the function/class to wh ...

Retrieve identification details from a button within an ion-item located in an ion-list

<ion-list> <ion-item-group *ngFor="let group of groupedContacts"> <ion-item-divider color="light">{{group.letter}}</ion-item-divider> <ion-item *ngFor="let contact of group.contacts" class="contactlist" style="cl ...

Is it possible to utilize a variable within the 'has-text()' function during playwright testing?

With Playwright, I am attempting to locate an element based on the value of a variable For instance: let username = 'Sully' await page.click(`li:has-text(${username})`) However, I encounter the following error: page.click: Error: "has-tex ...

Could not find the 'injectTapEventPlugin' export in the dependencies of Material-UI related to 'react-tap-event-plugin'

Currently, I am working on a project that involves using react, typescript, material-ui, and webpack. An issue has arisen with importing the injectTapEventPlugin function from the dependency of Material-UI, react-tap-event-plugin. The specific error messag ...