Can discriminated unions solely be utilized with literal types?

When looking at the code snippet below, I encountered an issue with discriminating the union type using the typeof operator.

function f(arg: { status: number; one: boolean } | { status: string; two: boolean }) {
    if (typeof arg.status === "number") {
        return arg // The argument is still of the union type and not narrowed down
    } else {
        return arg // Same situation here
    }
}

However, if the status property is changed to a literal type, then it becomes possible to discriminate the union.

function f(arg: { status: "one"; one: boolean } | { status: "two"; two: boolean }) {
    if (arg.status=== "one") {
        return arg // Now arg is of type { status: "one"; one: boolean }
    } else {
        return arg // And here it's of type { status: "two"; two: boolean }
    }
}

So, the question arises as to why it doesn't work in the first case. Is it because discriminated unions only function with literal types or could there be another reason?

I attempted to search through the documentation to see if it mentions anywhere that discriminated unions are exclusive to literal types, but I couldn't find any information on this.

Link to playground

Answer №1

As of now, Typescript faces a certain limitation that has been extensively debated within the Typescript Github community. I will draw upon insights shared in those discussions for this explanation.

One crucial aspect to understand is highlighted in this comment:

Type guards do not automatically update parent objects with narrowed types. The narrowing only affects the specific property accessed, explaining why destructuring works but referencing does not. Adapting the parent object would require creating new types, which can be resource-intensive.

The following code snippet showcases how TS can successfully narrow down the type of a particular property:

function f1(arg: { status: number; one: boolean } | { status: string; two: boolean }) {
    if (typeof arg.status === "number") {
        const st = arg.status // st: number
    }
}

Narrowing of the parent object occurs under specific conditions, particularly when the property serves as a discriminator within a union. A property is considered a discriminator if:

1- It represents a literal value, as detailed in Discriminated union types.

2- Within a union type, the property acts as a discriminator if it includes at least one unit type and no instantiable types, according to Allow non-unit types in union discriminants.

For additional information, refer to this link and this link.

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

Tips for identifying the load window in Ionic 2 RC3 Angular 2

In the various services that I am using, I implement a method to load content. Each service has its own loadController to display a loading window. How can I detect if a loading window already exists in order to prevent showing a new one? *Please note: I ...

What is the best way to retrieve all the keys from an array?

I am looking to retrieve the address, latitude, and longitude data dynamically: let Orders= [{ pedido: this.listAddress[0].address, lat: this.listAddress[0].lat, lng: this.listAddress[0].lng }] The above code only fetches the first item from the lis ...

Dealing with useEffect being invoked twice within strictMode for processes that should only execute once

React's useEffect function is being called twice in strict mode, causing issues that need to be addressed. Specifically, how can we ensure that certain effects are only run once? This dilemma has arisen in a next.js environment, where it is essential ...

Adjust the background color of the header as you scroll

Seeking assistance in adjusting the background color of my header upon scrolling. This is my current implementation: header.component.ts export class HeaderComponent { ngOnInit(): void { const header = document.querySelector('.header'); ...

What is the correct syntax for declaring a variable within a switch statement in TypeScript?

How can I properly use a switch statement in TypeScript to assign a new variable a value? For example: let name: string switch(index) { case 0: name = "cat" case 1: name = "dog" .... } I keep getting the err ...

Managing Prisma error handling in Express

Dealing with error handling using ExpressJS and Prisma has been a challenge for me. Anytime a Prisma Exception occurs, it causes my entire Node application to crash, requiring a restart. Despite looking at the Prisma Docs and doing some research online, I ...

I am facing a problem with React Hooks useRef where I am unable to retrieve the updated state value

Trying to use useRef with React hooks, I encountered an issue where the state of the child component changes when calling the setAccountVal method, but upon alerting the value it remains as "Ege". Any ideas on how to resolve this? import React, { useS ...

TypeScript and Next.js failing to properly verify function parameters/arguments

I'm currently tackling a project involving typescript & next.js, and I've run into an issue where function argument types aren't being checked as expected. Below is a snippet of code that illustrates the problem. Despite my expectation ...

Using Typescript to define the type for React's useState() setter function whenever

I'm working on setting up a React Context to handle parameters mode and setMode, which act as getter and setter for a React state. This is necessary in order to update the CSS mode (light / dark) from child components. I'm encountering a Typescr ...

Exploring the elements within the ContentChildren directive in Angular

Presenting my component: import { Component, OnInit, ContentChildren, QueryList } from '@angular/core'; import { IconBoxComponent } from '../icon-box/icon-box.component'; @Component({ selector: 'app-three-icon-box', temp ...

Having trouble getting a local npm installation to work from a specific file path?

After following the instructions from this helpful link to install an npm package through a file path, I encountered an error when attempting to use it: Cannot find module '<module_name>' or its corresponding type declaration Are there an ...

Enhancing external TypeScript modules

Currently, I am delving into the realm of enhancing external modules in TypeScript. I am diligently studying the official documentation and the DefinitelyTyped guides, poring over examples, and so forth. At this point, my goal is to incorporate custom prop ...

Incorporating Google Pay functionality within Angular applications

I have been attempting to incorporate Google Pay into my Angular project, but I am struggling to find reliable resources. My main issue revolves around the following code... <script async src="https://pay.google.com/gp/p/js/pay.js" onloa ...

Saving a JSON object to multiple JSON objects in TypeScript - The ultimate guide

Currently, I am receiving a JSON object named formDoc containing data from the backend. { "components": [ { "label": "Textfield1", "type": "textfield", "key": "textfield1", ...

Dynamic Setting of Content-Type Header (Multipart/Data) Through Http Interceptor

I have been trying to upload a CSV file using an HttpInterceptor as a middleware. Everything works fine for normal requests, but I need to modify the request header to 'multipart/data' specifically for CSV uploads. Below is the code snippet: ex ...

The state of dynamically created Angular components is not being preserved

My current task involves dynamically creating multiple components to be placed in a table. The code successfully achieves this objective, but the state seems to be getting jumbled up at the level of the dynamically generated components. When a component is ...

Discovering subtype relationships in JSON with TypeScript

Consider the scenario where there are parent and child typescript objects: class Parent { private parentField: string; } class Child extends Parent { private childField: string; } Suppose you receive a list of JSON objects for both types via a R ...

`Error importing react-markdown in Next.js 11.1 with TypeScript``

Having trouble with importing react-markdown in my next.js SSG project. When running npm run dev, I encounter an error that prevents me from proceeding to test in next export. I understand that react-markdown is an esm package, but I'm not sure how t ...

cycle through options of radio buttons

How can I display items of radio buttons, with the values of these items coming from a backend api? <div class="input-group col-md-9 input-group-sm"> <label>gender</label> </div> <!-- TO CORRECT ...

I am trying to replace the buttons with a dropdown menu for changing graphs, but unfortunately my function does not seem to work with the <select> element. It works perfectly fine with buttons though

I am currently working on my html and ts code, aiming to implement a dropdown feature for switching between different graphs via the chartType function. The issue I am facing is that an error keeps popping up stating that chartType is not recognized as a ...