What is the reason behind Typescript not narrowing generic union type components when they are eliminated by type guards?

Consider the following scenario where type definitions and function implementations are provided:

interface WithNumber {
  foo: number;
}

interface WithString {
  bar: string;
}

type MyType = WithNumber | WithString;

interface Parameter<C extends MyType = MyType> {
  container: C
} 

function isParameterWithNumberContainer(arg: Parameter): arg is Parameter<WithNumber> {
  return typeof (arg.container as WithNumber).foo === "number";
}

function test(arg: Parameter<WithNumber | WithString>) {
  if (isParameterWithNumberContainer(arg)) {
    arg.container.foo;
    return;
  }
  /*
   * Error:
   *   Property 'bar' does not exist on type 'MyType'.
   *     Property 'bar' does not exist on type 'WithNumber'.
   */
  arg.container.bar;
}

Why doesn't TypeScript narrow down the type of arg below the if-block secured with a type guard? It seems clear that arg can only be a Parameter<WithString>. However, TypeScript still considers it as a

Parameter<WithNumber | WithString>
, leading to an error when trying to access arg.container.bar.

TS Playground link

Answer №1

Despite my extensive search efforts, I have been unable to locate the appropriate documentation regarding type guards and their functionality within if statements. From what I gather, the type defined in the then branch is excluded from a union type present in the else branch. It seems that the exclusion is not based on an in-depth analysis of the type itself. Moreover, since your parameter consists of a generic type with a union as its parameter, nothing will be excluded in the else branch.

To address this issue, one potential solution could be to modify the argument to be a union type:

function isParameterWithNumberContainer(arg: Parameter): arg is Parameter<WithNumber> {
    return typeof (arg.container as WithNumber).foo === "number";
}

function test(arg: Parameter<WithNumber> | Parameter<WithString>) {
    if (isParameterWithNumberContainer(arg)) {
        arg.container.foo;
        return;
    }
    arg.container.bar;
}

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

A React component featuring a nested map function should always include a "unique key" prop for each element

I am trying to figure out how to assign a proper Key Value in this component: {var.map((building, index) => { const handles = building.buildingVertices.map((point) => { return ( <DrawingHandle key={`${i ...

ts:Accessing the state within a Redux store

In my rootReducer, I have a collection of normal reducers and slice reducers. To access the state inside these reducers, I am using the useSelector hook. Here is my store setup : const store = configureStore({reducer : rootReducer}); Main Reducer: const ...

What is the process for configuring PhpStorm to sync with TypeScript tsconfig.json in .vue files?

Vue.js (webpack + vueify) with TypeScript is my current setup. The ts configuration appears to be functioning, but only in .ts files. For instance, in tsconfig.json: "compilerOptions": { "strictNullChecks": false, So strictNullChecks works as expect ...

Taunting a specific occurrence inside a group

Good evening, I am currently in the process of developing tests for the TypeScript class shown below. My goal is to create a test that ensures the postMessage method of the internal BroadcastChannel is called. However, I am facing difficulties in setting ...

Unexpected JSON end causes issue with DELETE request in Next.js version 13

I am currently working on a web app using Next 13 and I have a route.ts file located in the api folder. This file contains two different methods, POST and DELETE. While both methods successfully receive the request, I am facing an issue with JSON parsing ...

React: a versatile and type-specific onChange() function

After adding as HTMLInputElement, the error message of Property 'checked' does not exist on type 'EventTarget & (HTMLInputElement | HTMLTextAreaElement)' is resolved. However, I find it interesting that TypeScript doesn't autom ...

Is it considered best practice to pass an argument that represents an injectable service?

Is it a good practice to pass an argument that is an injectable service to a function? Hello everyone, I have been doing some research but have not found a definitive answer to the question above yet. I am currently working with Angular and came across so ...

Analyzing a string using an alternative character

I want to convert the string "451:45" into a proper number. The desired output is 451.45. Any help would be appreciated! ...

Strategies for capturing a module's thrown exception during loading process

Is there a way to validate environment variables and display an error message on the page if the environment is found to be invalid? The config.ts file will throw an exception if the env variable is invalid. import * as yup from 'yup' console. ...

Set up an event listener for when geolocation permission is approved

My Setup: I've written some basic code snippet below: const onSuccess = () => { console.log('success'); } const onError = () => { console.log('error'); } navigator.geolocation.getCurrentPosition(onSuccess, onError) ...

What does the reportProgress function do in HTTP services with JavaScript?

Can someone explain the functionality of reportProgress in JavaScript, specifically when used with Angular Typescript? I am having trouble finding documentation on this. return this.httpClient.request<ProductResponse>('get',`${this.basePath ...

What is the best method for retrieving an item from localstorage?

Seeking advice on how to retrieve an item from local storage in next.js without causing a page rerender. Here is the code snippet I am currently using: import { ThemeProvider } from "@material-ui/core"; import { FC, useEffect, useState } from "react"; i ...

Using Firebase with Angular 4 to fetch data from the database and show it in the browser

Currently diving into Angular 4 and utilizing Firebase database, but feeling a bit lost on how to showcase objects on my application's browser. I'm looking to extract user data and present it beautifully for the end-user. import { Component, OnI ...

Tips for finding the displayRows paragraph within the MUI table pagination, nestled between the preceding and succeeding page buttons

Incorporating a Material-UI table pagination component into my React application, I am striving to position the text that indicates the current range of rows between the two action buttons (previous and next). <TablePagination ...

Working with a function in the stylesheet of TypeScript in React Native allows for dynamic styling

When attempting to use variables in my StyleSheet file, I encounter a type error even though the UI works fine. Any suggestions on how to resolve this issue? type buttonStyle = (height: number, width: string, color: string) => ViewStyle; export type St ...

Can someone point me to the typescript build option in Visual Studio 2019 Community edition?

When I created a blank node.js web app on VS 2015, there was an option under project properties called "TYPESCRIPT BUILD" that allowed me to configure settings like combining JavaScript output into a single file. After upgrading to VS 2019 Community, I ca ...

Discovering a variable within an enzyme wrapper for the locate function

Struggling through testing with jest + enzyme. I have an array called OptionsArray that contains options mapped to buttons in a component. In my testing suite for the component, I attempted to do the following: import React from 'react'; import { ...

Dealing with nullable properties in Typescript

In my React Component code snippet, I am facing an issue with an optional field that needs to be initialized as undefined. This is causing difficulties when trying to use it after type checking. The problem arises in the context of using typescript version ...

Tips for incorporating a child's cleaning tasks into the parent component

I am working with a parent and a child component in my project. The parent component functions as a page, while the child component needs to perform some cleanup tasks related to the database. My expectation is that when I close the parent page/component, ...

Unexpected Typescript error when React component receives props

I encountered an unexpected error saying ": expected." Could it be related to how I'm setting up props for the onChange event? Here is my code for the component: import React from "react"; interface TextFieldProps { label?: string; ...