The narrowing of Union types in Typescript is not possible when it is based on

I'm facing an issue with narrowing down a union type in typescript.

Let's say we have two interfaces and a union:

interface A {
    flag: true
    callback: (arg: string) => void
}

interface B {
    flag?: false
    callback: (arg: number) => void
}

type AB = A | B

This is narrowed down correctly:

const testA: AB = {
    flag: true,
    callback: arg => {
        // typescript recognizes this as interface A and arg as a string
    }
}

const testB: AB = {
    flag: false,
    callback: arg => {
        // typescript identifies this as interface B and arg as a number
    }
}

However, this scenario does not work:

const testC: AB = {
    // flag is implied as undefined
    callback: arg => {
        // typescript cannot determine if this is A or B
        // arg is implicitly any
    }
}

What could I be missing?

Here is a link to typescript playground

Thank you in advance

Answer №1

// Encountered an error on testC, where 'number' was not accepted as a type for 'string'
const testC: AB = {
  flag: undefined,
  callback: (arg: string) => {
  }
}

// The same error occurred again
const testC: AB = {
  callback: (arg: string) => {
  }
}

Interestingly:

interface B {
  flag: false | undefined;
  callback: (arg: number) => void
}

// Received an error saying that the type '{ callback: (arg: any) => void; }' cannot be assigned to type 'AB'.
//   It was expected to have property 'flag' in type '{ callback: (arg: any) => void; }' but it is missing, which is required as per type 'B'.
const testC: AB = {
  callback: (arg) => {
  }
}

I find it curious that there might be a peculiar behavior related to how Typescript handles flag? versus flag: undefined. This could potentially be linked to an uncommon behavior that people are not accustomed to in JavaScript:

Object.keys({a: undefined}); // ['a']
Object.keys({}); // []

Answer №2

Using flag? signifies the potential non-existence of a value.

When flag? is set to false, it actually translates to flag: false | undefined, rather than simply indicating the absence of the flag property.

const exampleVar: XYZ = {
    flag: undefined,
    callback: argument => {
        // TypeScript recognizes that this falls under interface B and that argument is a number
    }
}

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

Expanding a specific segment of a discriminated union

I have a discriminated union structure that I am working with: type Union = { // This is fascinating! The discriminant value can also be another union! type: "foo" | "bar" } | { type: "baz" } | { type: "qu ...

Postman is having trouble communicating with an express router and is unable to send requests

Currently, I am experiencing some challenges while trying to grasp the concepts of express and node with typescript, particularly when setting up a router. In my bookRoutes.ts file, I have defined my router in the following manner: import express, { Expre ...

Filtering arrays of objects dynamically using Typescript

I am looking to create a dynamic filter for an array of objects where I can search every key's value without specifying the key itself. The goal is to return the matched objects, similar to how the angular material table filters all columns. [ { ...

Using React and Typescript: Populating an array within a For Loop using setTimeout is not happening sequentially or at all

I'm currently working on a function that animates images of random cars moving across the screen. My goal is to stagger the population of the "carsLeft" array using setTimeout, where I will eventually randomize the delay time for each car. Everything ...

Stop the print dialog box from appearing when using the Ctrl + P shortcut

I'm working on an Angular app and I want to prevent the print dialog from opening when pressing "Ctrl + P". To address this issue, I have implemented the following code: window.onbeforeprint = (event) => { event.stopPropagation(); cons ...

What is the reason behind TypeScript prohibiting the assignment of a className property to a context provider?

Embarking on my first journey with TypeScript, I am in the process of reconfiguring a React application (originally built with create-react-app) to use TS. Specifically, I am working with function components and have introduced a global context named Order ...

Using Ionic 3 to create a list view that includes buttons linked to the items clicked

I need assistance with modifying the button icon in a list layout: <ion-list inset *ngFor="let audio of event.audios; let i = index"> <ion-item> <div class="item-text-center item-text-wrap"> {{audio.fileName}} </div& ...

Navigating through pop-ups in Chrome: A guide to using Protractor

Currently, I am utilizing Protractor and am faced with the challenge of handling a pop-up from Chrome. My goal is to successfully click on the button labeled "Open magnet URI". For a visual representation of the issue, refer to the following image: picture ...

Converting JSON objects into TypeScript classes: A step-by-step guide

My challenge lies in converting Django responses into Angular's User array. This conversion is necessary due to variations in variable names (first_name vs firstName) and implementing specific logic within the Angular User constructor. In simple term ...

Having trouble opening a modal view from an action sheet button

Currently, I am in the process of working on a school project and facing an issue where I am trying to open a modal view from an action-sheet button. However, I encounter the following error message: TypeError: Cannot read property 'addMedicationModal ...

Tips for testing the setTimeout function within the ngOnInit using Jasmine

Could someone please assist me with writing a test for an ngOnInit function that includes a setTimeout() call? I am new to jasmine test cases and unsure of the correct approach. Any guidance would be greatly appreciated. app.component.ts: ngOnInit(): void ...

Utilizing Gulp to Convert TypeScript Exports into a JSON File

I have a set of TypeScript files, some of which export a specific variable - named APIS - which contains an array of objects. My goal is to extract the values from all of these exports and save them into a JSON file using Gulp. Let's consider a direc ...

Error encountered when running protractor cucumber test: SyntaxError due to an unexpected token {

Embarking on the journey of setting up Protractor-Cucumber tests, I have established a basic setup following online tutorials shared by a kind Samaritan. However, upon attempting to run the tests, I encountered an error - unexpected token for the imports. ...

The properties of the extended Array class in TypeScript are not able to be accessed

It might be the late hour or my brain overloaded with programming, but I'm completely puzzled by this simple class: export class Path extends Array { constructor(...params:Array<any>) { super(...Object.assign([], arguments)); } ...

Concealing a column within an Angular Material table

I'm currently faced with a challenge involving an Angular Material table containing numerous columns. My goal is to selectively hide certain columns based on specific CSS media queries. This is the code snippet I have experimented with so far: HTML: ...

Utilizing React with Typescript: A guide to working with Context

I have a super easy app snippet like import React, { createContext } from 'react'; import { render } from 'react-dom'; import './style.css'; interface IAppContext { name: string; age: number; country: string; } const A ...

Function input custom operator in RxJs

I am currently working on developing a custom rxjs operator. My previous custom operators, such as MonoTypeOperatorFunction or the regular Observable that accepts input like strings or numbers, have been successful. However, I am facing a challenge with cr ...

What steps should I take to choose esNext from the typescript menu within visual studio 2017?

When utilizing dynamic import with TypeScript in Visual Studio 2017, I encountered the following error: TS1323(TS) Dynamic imports are only supported when the '--module' flag is set to 'commonjs' or 'esNext'. I attempted to c ...

Angular - optimizing performance with efficient HTTP response caching tactics

I'm managing numerous services that make requests to a REST service, and I'm looking for the optimal method to cache the data obtained from the server for future use. Can someone advise on the most effective way to store response data? ...

No overload error encountered with TypeScript function call

I am working on an async function that communicates with the backend and I need it to handle axios error messages. My goal is to display these messages in a form. export async function register( prevState: string | undefined, formData: FormData ) { t ...