Type narrowing is not retained in subsequent Array.prototype.map operations

I'm struggling to understand the type checking errors in the code snippet below:

interface Foo {
    kind: 'foo';
    a: string;
}

interface Bar {
    kind: 'bar';
    b: number;
}

type FooBar = Foo | Bar;

interface Container {
    ids: number[];
    fooBar: FooBar;
}

const cont: Container = {
    ids: [1, 2],
    fooBar: { kind: 'foo', a: 'a' },
};

switch (cont.fooBar.kind) {
    case 'foo':
        console.log(cont.fooBar.a); // This line is fine

        cont.ids.map((id) => {
            console.log(`${id} - ${cont.fooBar.a}`);
            // Getting error: Property 'a' does not exist on type FooBar nor Bar
        })
        break;
    case 'bar':
        console.log(cont.fooBar.b); // This line is also fine

        cont.ids.map((id) => {
            console.log(`${id} - ${cont.fooBar.b}`);
            // Getting error: Property 'b' does not exist on type FooBar nor Foo
        })
        break;
}

For a live demonstration of this issue, check out this playground example.

Answer №1

To resolve this issue, it is recommended to assign the value of fooBar to a local constant variable using const, and then utilize fooBar directly in the switch statement:

const { fooBar } = cont; // <====
switch (fooBar.kind) {
    case 'foo':
        console.log(fooBar.a); // Fine

        cont.ids.map((id) => {
            console.log(`${id} - ${fooBar.a}`); // Now works correctly
        })
        break;
    case 'bar':
        console.log(fooBar.b); // Good

        cont.ids.map((id) => {
            console.log(`${id} - ${fooBar.b}`); // Now outputs as expected
        })
        break;
}

View Live Example

Initially, I attempted this approach within each case, but even when implemented as shown above, TypeScript still responds positively.

I suspect that TypeScript's uncertainty about the callback being synchronous plays a role here. If other parts of the code modify cont.fooBar asynchronously after the callback, the narrowed type may no longer be accurate. By storing it locally, any uncertainty is eradicated. This hypothesis is further supported by the fact that changing the assignment from const { fooBar } = cont; to let { fooBar } = cont; causes the narrowing to fail once more.

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

The name property of event.currentTarget is now being returned as currentTarget

I am facing an issue with my handleChange function in typescript. When I try to retrieve the name attribute from a text field and log it, it returns 'currentTarget' instead of the assigned name. Additionally, the value is showing up as undefined. ...

How to resolve the issue of not being able to access functions from inside the timer target function in TypeScript's

I've been attempting to invoke a function from within a timed function called by setInterval(). Here's the snippet of my code: export class SmileyDirective { FillGraphValues() { console.log("The FillGraphValues function works as expect ...

Error: The file named '/accounts.ts' cannot be recognized as a module within a Node.js API

After researching this issue, I have found some answers but none of them seem to solve my problem. Below is the code in my model file: // accounts.ts const mongoose = require('mongoose'); var autoincrement = require('simple-mongoose-autoi ...

Issue encountered while trying to insert a new row into the mat-table

I need help with inserting a new row in mat-table using a button. I wrote a function for this, but when I click the button, I encounter an error CalculatoryBookingsComponent.html:62 ERROR Error: Cannot find control with path: 'rows -> 0'. Addi ...

Angular 11 along with RxJS does not support the combineLatest method in the specified type

Hey there, I'm currently working on utilizing the combineLatest operator to merge two streams in Angular, but I keep encountering an error message stating that "combineLatest does not exist on type". I've attempted to move the code into a .pipe() ...

Filter that caters to specific number of properties of X

Looking to create a versatile filter function that can handle multiple criteria? Here's a snippet of the current filtering function: const filterRows = () => { items.filter((item) => { if(selectedDrinks.length > 0 && select ...

Mapping the preselected values of multiple input fields into an array in Angular 6: A step-by-step guide

When submitting a form with input fields, I need to capture the selected values into an array format like {"userid":1,"newstatus":[1],"mygroup":[1,2,3]}. I attempted using ngmodel but encountered issues. Below is the code snippet: home.component.html & ...

The parameters 'event' and 'event' are not compatible with each other due to their different types

I am currently working on an employee monitoring project that consists of multiple components. One specific component involves grouping together a set of buttons. While integrating these buttons in another component, I encountered an error in my code: The ...

Discovering the versatility of Typescript objects

I want to define a type that follows this rule: If the property container is present, then expect the property a. If the property item is present, then expect the property b. Both container and item cannot exist at the same time. The code I would expect ...

In Typescript and Angular 9, it is important to ensure that all code paths are complete and return

Currently, I am in the process of developing a small project that involves registration and authentication using Express alongside Angular 9. Everything was progressing smoothly until I encountered the error Not all code paths return a value in the file re ...

What is the best way to save data from a Firebaselistobservable into an array?

I've been attempting to transfer data from Firebase to an array using Angular 2, but I'm facing difficulties in pushing the data into the array. Below is the code snippet: Variables: uid: string = ''; agencyItems: FirebaseListObserva ...

An issue occurred: 'Problem encountered while cleaning up component', Object{component: FilterComponent}

When running the test for FilterComponent, I keep encountering this error: Error 'Error during cleanup of component', Object{component: FilterComponent{lang: Language{cookie: ..., get: ..., getLocale: ..., dict: ...}, api: Api{http: ...

The program experienced an issue with TypeError: Attempting to access properties of an undefined variable ('_id')

I am attempting to show a data entry (with a unique id) in Angular, but I'm encountering the following error: ERROR TypeError: Cannot read properties of undefined (reading '_id') The service for retrieving the specific id is defined as: g ...

Utilizing PropTypes in React with TypeScript

I've encountered issues while trying to integrate PropTypes with Typescript: Previously, without typescript, I had successfully used: class TodoFilterItem extends Component { constructor (props) { super(props); Followed by: TodoFilterItem.prop ...

Can we use type mapping and conditional types to make it mandatory for an object to have certain specified keys?

Someone sent over a type definition from a 3rd party library: interface UpdateRequest { data?: ValueRange[]; options?: Options } I am trying to implement a method with the following signature: update(params: RequiredOnly<UpdateRequest, 'dat ...

Send properties to the component container

I am currently working on a higher order component that toggles between two children - a component that renders data and a loading component. The challenge I am facing is how to pass the loading state from the data component to the HOC for conditional rend ...

Deactivate the Mention and Hash tag in ngx-linkifyjs

I am currently utilizing ngx-linkifyjs to automatically convert URLs in text to clickable hyperlinks. However, I am facing an issue where it is also converting # and @ tags into links. Is there a way to prevent the conversion of # and @ while maintain ...

Does my pseudo example for react event pooling seem logical?

TLDR Looking for insights on implementing event pooling logic in React. Curious to understand the principles behind event pooling :) Question While exploring the depths of the React documentation, I stumbled upon event pooling. Intrigued by this concep ...

The continuous re-rendering is being triggered by the Async/Await Function

I am facing an issue with fetching data from the backend using axios. The function is returning a Promise and each time I call it, my component keeps rendering continuously. Below is the code snippet: import { useState } from "react"; import Ax ...

What is the best way to specify a function type that takes an argument of type T and returns void?

I am in search of a way to create a type that can accept any (x: T) => void function: let a: MyType; a = (x: number) => {}; // (x: number) => void a = (x: string) => {}; // (x: string) => void a = (x: SomeInterface) => {}; / ...