Is the type narrowed by type guards only when true is returned?

It was my understanding that a type guard handling multiple types

instanceOfA(arg: A | B | C): arg is A
, would narrow the type to either A (if the guard returns true) or B | C (if it returns false)

However, in the case of instanceOfB below, when returning false the type seems to be narrowed to never, while returning true doesn't narrow it at all. Do type guards really only narrow the type if true is returned? Or am I misinterpreting the outcomes, and if so, why does the narrowing to never occur?

// Structures
interface A {
    value: string
    children: B[]
}

interface B {
    value: string
}

// Example functions
function doSomething1(arg: A | B) {
    if (instanceOfA(arg)) {
        console.log(`A: ${arg.value}`) // Type of arg is A
    } else {
        console.log(`B: ${arg.value}`) // Type of arg is B
    }
}

function doSomething2(arg: A | B) {
    if (instanceOfB(arg)) {
        console.log(`B: ${arg.value}`) // Type of arg is A | B
    } else {
        console.log(`A: ${arg.value}`) // Type of arg is never
    }
}

// Typeguards
function instanceOfA(ab: A | B): ab is A {
    return ab.hasOwnProperty('children')
}

function instanceOfB(ab: A | B): ab is B {
    return !ab.hasOwnProperty('children')
}

Answer №1

Is the issue at hand a theoretical puzzle or a real-world problem? It seems like labeling a type based on what it lacks may not be the most accurate approach.

If we refer to the narrowing section in the typescript book, they provide an example where a type guard is used to distinguish between a Fish and a Bird by checking if the pet can swim (which might be somewhat of an oversimplification). On the other hand, the logic you are using translates to: "If it can't swim, then it must be a bird," which doesn't seem entirely logical (unless the only options are fish and birds).

The use of instanceOfB appears questionable. TypeScript allows for assigning different types to one another as long as there is enough overlap, and A will always be assignable to

B</code because it encompasses all its properties. The discrepancy would only arise once a new property is added to <code>B</code that isn't present in <code>A</code.</p>
<p>Consider this valid TypeScript example:</p>
<pre><code>const a: A = {value: 'this is a', children: []};
const b: B = {value: 'this is b'};

function printValue(subject: {value: string}) {
    console.log(subject.value);
}

Your check could begin functioning if you made a modification like this:

interface B {
    value: string
    other: string;
}

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

Issue regarding custom CSS implementation in Angular project

Being a French speaker, I apologize in advance for any mistakes I might make. I am also fairly new to Angular, so if you could provide detailed explanations in your responses, it would be greatly appreciated. Thank you. I am trying to import a custom CSS ...

What is the best way to update properties in a child component using React hooks?

Looking to update the props using react hooks, I came across a method of passing setState function as props to the child component. MainContainer.tsx const MainContainer: React.FC = () => { const [count, setCount] = useState(0); return <Counter ...

Too many open files error encountered in Watchpack (watcher) - NextJS

Encountering an issue with watchpack resulting in the error messages shown above while running a next app using next dev. The error message is displayed continuously on the screen as follows: Watchpack Error (watcher): Error: EMFILE: too many open files, w ...

Experimenting with the routerLink directive in Angular 2

Currently, I am in the process of testing routing functionality. As part of this, I have moved my navbar to a separate component called MdNavbar, which primarily consists of HTML and CSS. The RouteConfig is located in another component where MdNavbar is in ...

calculate the difference between two dates and then add this difference to a new date

Utilizing TypeScript for date calculations. Example: Initial Date 1: "10/06/2021 10:10:05" Initial Date 2: "08/06/2021 11:10:05" Calculate the difference between the two dates, including date/month/year/hour/min/sec/milliseconds. Ensure compatibility wi ...

I'm unable to import correctly using the --compiler option

Having an issue here. I'm trying to bring in the typescript compiler. I used this command: bit import bit.envs/compilers/typescript --compiler Unfortunately, it didn't work. This is the error message: bit import [ids...] import components in ...

Angular has a built-in function to determine the next ID after deletion of some items

I am currently facing a situation where I have a list of contacts in a detailed view and navigating through them using the following code: <nav> <a [routerLink]="['/details', friend.id - 1 ]" *ngIf="!(friend.id == 1)"> P ...

How can I fully mock a component in an Angular Unit Test, rather than just a single class?

In my current task, I am faced with the challenge of unit testing a component that relies on a subcomponent. The subcomponent utilizes CanvasJS for displaying plots, but this poses issues when running Jest Unit Tests. As of now, in my spec file, the follo ...

Navigate to a different directory within Cypress by utilizing the cy.exec() function

Dealing with cypress to execute a python script in another directory. However, it seems like the directory change is not functioning as expected. visitPythonProject() { cy.exec("cd /Users/**/Desktop/project/"); cy.exec("ls"); // thi ...

Determine the data type of a variable in TypeScript by utilizing the compiler API

I am facing an issue with retrieving the type of a variable using the compiler API. Here is some background information on my situation: Since I execute everything in memory, I have had to create my own compiler host. This is necessary because the typechec ...

Is there a way to display the number of search results in the CodeMirror editor?

After conducting some research on how to integrate the search result count in Codemirror like the provided image, I was unable to find any solutions. I am currently working with Angular and utilizing ngx-codemirror, which led me to realize that editing the ...

What is the correct way to define an abstract method within a class to ensure that an IDE detects and notifies if the abstract method is not implemented

Is there a way to properly define an abstract method in an abstract class and have the IDE notify us if we forget to implement it? I attempted the following approach, but it did not work: export abstract class MyAbstractClass { /** * @abstract ...

Angular unable to send object to HTML page

Struggling with learning angular, encountering new challenges when working with objects in different components. In two separate instances, try to implement two different mechanisms (microservice or service component serving an object directly). This speci ...

Jest.useFakeTimers() causing clearTimeout test to malfunction

I've been exploring the transition to the newer version of jest.useFakeTimers(), which is not set as default in the latest 27.x release. Encountering some issues during my tests, Jest keeps flagging that functions like clearTimeout and setInterval a ...

Issue with jsPDF: PNG file is either incomplete or corrupted

I'm encountering an issue while attempting to pass Image data to the addImage function. I have tried downgrading the versions of jspdf and html2canvas, as well as experimenting with different ways to import the two libraries, but the problem still per ...

Struggling with module dependencies in Nest.js

I have been diving into the documentation provided on the NestJs website, but I've noticed that it can be a bit scattered. My goal is to retrieve an RPG Character from a Mongo database using TypeORM. Unfortunately, I seem to be running into dependency ...

Incorporating a Script into Your NextJS Project using Typescript

I've been trying to insert a script from GameChanger () and they provided me with this code: <!-- Place this div wherever you want the widget to be displayed --> <div id="gc-scoreboard-widget-umpl"></div> <!-- Insert th ...

What is the best way to execute multiple functions simultaneously in Angular?

Within a form creation component, I have multiple functions that need to be executed simultaneously. Each function corresponds to a dropdown field option such as gender, countries, and interests which are all fetched from the server. Currently, I call th ...

Error: Protractor encountered an unexpected token while trying to import an external class

While working on my Protractor test, I encountered a syntax error on import when trying to bring an external class into the test. The strange thing is that the error only occurs at runtime, even though I am confident that I am importing and exporting the c ...

When using Angular 5's ngModel, the user interface displays the updated value dynamically without requiring the

When filling out my form, I encounter an issue with a select element and a bind variable. If I make a change to the value and save it, everything works as expected. However, if I make a change in a modal window but then close the modal without saving the v ...