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

Unable to locate the "fcm-node" module in Node.js with TypeScript

When working on a TypeScript project, I usually rely on the fcm-node package to send Firebase push notifications in Node.js. However, this time around, I faced an issue. I know that for TypeScript projects, we also need to install type definitions (@types ...

Angular 2: A guide to setting up `--module system --experimentalDecorators` flags in your project

I am encountering an issue while compiling my TypeScript file: app/app.component.ts import {Component} from 'angular2/core'; @Component({ selector: 'my-app', template: '<h1>Messenger</h1>' }) export clas ...

How can we ensure that only one of two props is specified during compilation?

I've designed a customized Button component. interface Button { href?: string; action(): void; } I'm looking to ensure that when a consumer uses this Button, they can only pass either href or action as a prop, not both. I want TypeScri ...

Error message indicating that an object may be undefined in a section of code that cannot possibly be reached by an undefined value

Does anyone have a solution for resolving the Typescript error message "Object is possibly 'undefined'" in a section of code that cannot be reached by an undefined value? This area of code is protected by a type guard implemented in a separate fu ...

Utilizing constants within if statements in JavaScript/TypeScript

When working with PHP, it is common practice to declare variables inside if statement parenthesis like so: if ($myvar = myfunction()) { // perform actions using $myvar } Is there an equivalent approach in JavaScript or TypeScript?: if (const myvar = myf ...

Show just a single error message if there are two validation errors present

In my AngularJS timepicker, users can choose multiple time segments for each day. The code has validation to detect duplicates and overlapping time segments. For example, entering 11:00am - 12:00am twice will trigger two error messages: 'Overlapping t ...

"Error: The property $notify is not found in the type" - Unable to utilize an npm package in Vue application

Currently integrating this npm package for notification functionalities in my Vue application. Despite following the setup instructions and adding necessary implementations in the main.ts, encountering an error message when attempting to utilize its featur ...

Exploring the power of developing AngularJS applications using Typescript and the app

I am currently working on a basic application that utilizes Typescript and AngularJS. In my app, I have used app.js to register my controller and some route parameters: /// <reference path="../scripts/typings/angularjs/angular.d.ts" /> /// <refer ...

How can I display data both as a dropdown and an autocomplete in Angular using a textbox?

There is a textbox with autocomplete functionality. When the user clicks on the textbox, an API call is made with two parameters - Pubid and Date. The data is then displayed in a dropdown with autocomplete feature. Now I am attempting to have the data app ...

"Transferring a C# dictionary into a TypeScript Map: A step-by-step

What is the correct way to pass a C# dictionary into a TypeScript Map? [HttpGet("reportsUsage")] public IActionResult GetReportsUsage() { //var reportsUsage = _statService.GetReportsUsage(); IDictionary<int, int> te ...

Issue with React Redux: Store dispatch not causing component update

I have recently implemented React Redux in my project, but I seem to be encountering some issues. Despite changing the state, the value remains the same. I attempted to use useStore(), but it does not take any parameters. Can anyone provide insight into wh ...

Exploring the capabilities of TypeScript in conjunction with the useRoute function on React Navigation version

When using useRoute, I am attempting to extract the parameters like this: const route = useRoute(); const { params } = route; const { id, name, } = params; Although everything is functioning correctly, the linter is pointing out that id and na ...

Implementing Typescript with React: Assigning event.target.name to state

I am facing an issue with a React state that has specific named keys defined in an interface... Despite trying a potential solution based on the state keys, I am still encountering an error... { [x: string]: string; }' provides no match for the sign ...

Unable to simulate the navigator.language

I'm currently in the process of writing unit tests for some of my shared utility functions. As someone who is relatively new to unit testing, I am encountering difficulties when trying to mock certain global objects. Specifically, I am struggling to f ...

Angular 6 is throwing an error message stating that it cannot access the 'image' property of an undefined object

Having trouble retrieving the details, as it is rendering to the dom with an undefined error. Check out this image for reference: Welcome to the Book Details Component export class BookDetailsComponent implements OnInit { book: Book; books: Book[]; ...

Is it possible to verify if a function is invoked using Jest, Typescript, and ts-jest in action?

Currently, I'm testing the functionality of this code snippet: src/helpers/CommentHelper.ts: export default class CommentHelper { gitApiObject: GitApi.IGitApi ; constructor(gitApiObject: GitApi.IGitApi) { this.gitApiObject = gi ...

Discovering the world of Promises in TypeScript and understanding how to return specific types

Transitioning from coding in Clojure for the past two years to TypeScript has been an interesting journey. However, I've hit a bit of a roadblock today. The issue lies with my interface: interface ICustomer { id: number, first_name: string } I ...

Having trouble typing a RequestHandler in Express JS using TypeScript?

I am trying to create a RequestHandler that types the req.params and req.body with these interfaces interface UpdateNoteParams { noteId: string, } interface UpdateNoteBody { title?: string, text?: string, } This is what I have tried so far: e ...

What is the best way to pass a URL as a prop in Next.js without encountering the issue of it being undefined

Within my file (handlers.ts), I have a function designed to post data to a dynamic API route while utilizing the app router. This function requires values and a URL for fetching. However, when I pass the URL as a prop like this: http://localhost:3000/unde ...

Using the hook to implement the useContext function in React

I came across this definition export interface user{ email:string name:string last_name:string } export type UserType= { user: user; setUser:(user:user) => void; } const [user,setUser] = useState <user> ({ email ...