Switch statements in TypeScript may not function properly with type guards when assigning an object to a variable

I'm puzzled as to why the type guard is not working in the example provided below...

Considering the following interfaces:

interface ParamA {
    name: 'A';
    aaa: boolean;
}

interface ParamB {
    name: 'B';
    bbb: number;
}

Working Properly

function func(param: ParamA | ParamB) {
    switch (param.name) {
        case 'A':
            const aaa = param.aaa;
            console.log(aaa); // boolean
            break;
        case 'B':
            const bbb = param.bbb;
            console.log(bbb); // number
            break;
        default:
            break;
    }
}

Not Working Properly

function func(param: ParamA | ParamB) {
    const name = param.name; // just rewrite here
    switch (name) {
        case 'A':
            const aaa = param.aaa;
           
            console.log(aaa);
            break;
        case 'B':
            const bbb = param.bbb;
           
            console.log(bbb);
            break;
        default:
            break;
    }
}

The compiler throws errors like

Property 'aaa' does not exist on type 'ParamB'.
I fail to see why there would be a difference in behavior depending on whether it's stored in a variable or accessed directly.

This code is using TypeScript version 2.8.3.

Could someone shed some light on this issue?

Answer №1

Latest Update as of August 2021:

The latest version of Typescript (4.4) has introduced smarter features, making your examples work seamlessly without any additional configurations required :-)

Prior Response

When you assign a property to a variable, it breaks the connection with the object. This can be confusing for the compiler since you could alter the string and object variable independently:

var param: ParamA | ParamB;
param = someParamA;
const name = param.name;
param = someParamB;
switch (name) { // potential issues here

If you are certain that the variable has not been changed, you may consider using type assertion, although I recommend avoiding it for easier maintenance.

Answer №2

switch statement acts as a type guard in the 'good' code snippet, but not in the 'bad' one.

I believe there should be consistency in behavior regardless of whether I assign it to a variable or not.

The difference lies not in behavior, but in how TypeScript interprets the code. Due to static analysis, the ways in which the type of param can be narrowed down are restricted.

In order for param to be inferred as either ParamA or ParamB, it must be explicitly stated in a type guard. Since this was not done for param but was done for the name variable, param will not be narrowed down and its type will need to be asserted in such cases:

...
switch (name) {
    case 'A':
        const aaa = (<ParamA>param).aaa;
...

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

Error: Trying to access the 'blogpost' property of an undefined variable results in a TypeError while executing the NPM RUN BUILD command in Next.js

Encountering a frustrating issue while trying to run my Next.js application for production build. The problem seems to be related to the "blogpost" parameter in the following codeblock: import React from "react"; import Slab from "../../comp ...

Utilizing dynamic arguments in TypeScript to recycle types

Can I accomplish this with TypeScript? Here is the source code I currently have: interface FormStore<T> { type: T; } interface Form<T> extends FormStore<T> { email: T; phone: T; password: T; } interface FormState<R> { fo ...

Error: Model attribute missing in Adonis JS v5 relationship

Recently, I started diving into the Adonis framework (v5) and decided to build a todo list api as part of my learning process. However, I'm facing an issue concerning the relationship between the User and Todo entities. Let me show you the models fo ...

"An issue has been identified where TSLint and VSCode fail to display red underlines in

I am currently working on a single .ts file where I am experimenting with configuring tslint and tsconfig. To test the configuration, I intentionally added extra spaces and removed semicolons. Despite running the command tslint filename.ts and detecting e ...

The essential guide to creating a top-notch design system with Material UI

Our company is currently focusing on developing our design system as a package that can be easily installed in multiple projects. While the process of building the package is successful, we are facing an issue once it is installed and something is imported ...

Attempting to retrieve a file from the database with the utilization of Angular 5 and ASP.NET Core

I've been struggling with downloading a file from the database using its unique identifier. Can anyone provide guidance on what steps are necessary to achieve this successfully? The FileUpload table contains the following columns pertaining to the fi ...

Encountering a type discrepancy issue when attempting to send a message to the SQS queue

Encountering a type mismatch error while attempting to send a message to the SQS queue using the documentation found at https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_sqs_code_examples.html. I have provided package details and ...

Verify in Typescript if there is at least one value supplied

Looking for a solution: function takeOneOfOrThrow(firstOptionalVariable : string | undefined, secondOptionalVariable : string | undefined) { let result : string; if (!firstOptionalVariable && !secondOptionalVariable) { throw new E ...

Tips for making use of incomplete types

Is there a way to reference a type in TypeScript without including all of its required fields, without creating a new interface or making all fields optional? Consider the following scenario: interface Test { one: string; two: string; } _.findWhe ...

How to trigger a click event in React using TypeScript and material-ui library

Currently, I am facing an issue when trying to update the value of material-ui TextFields from the store. When manually typing inside the field, everything works fine as expected with the handleChange() and handleBlur() functions handling the events. Howev ...

The solution to automatically delete orphaned rows in TypeORM

Having a one-to-many relationship in TypeORM, I am interested in deleting rows from the many side of the connection rather than just unlinking them and leaving orphaned entries. Can anyone suggest a way to achieve this since the proposed feature for it w ...

Jest does not support the processing of import statements in typescript

I am attempting to execute a simple test. The source code is located in src/index.ts and contains the following: const sum = (a, b) => {return a+b} export default sum The test file is located in tests/index.test.ts with this code: impor ...

What is the best way to assign the value of an HTTP GET request to a subarray in Angular 8

Attempting to store data in a sub-array (nested array) but despite receiving good response data, the values are not being pushed into the subarray. Instead, an empty array is returned. for (var j=0;j<this.imagesdataarray.length;j++){ this.http.g ...

using props as arguments for graphql mutation in react applications

Here is the structure of my code: interface MutationProps{ username: any, Mutation: any } const UseCustomMutation: React.FC<MutationProps> = (props: MutationProps) => { const [myFunction, {data, error}] = useMutation(props.Mutati ...

Receiving JSON data in TypeScript and displaying it in a structured table format

I am just starting to learn TypeScript, I need to be able to handle JSON data of varying sizes... Once I receive the data, I want to display it in a table format... The structure of my JSON data will resemble this: [{"regionID":1 "regionname":"Can"}, ...

Combining all code in Electron using Typescript

I am currently working on developing a web application using Electron written in Typescript and I am facing some challenges during the building process. Specifically, I am unsure of how to properly combine the commands tsc (used to convert my .ts file to ...

Guide on transferring data from one table to another by choosing one or multiple rows with the help of Angular 2

Hello, I'm currently facing an issue with my requirements. I'm attempting to display two tables. Data for Table One: TableOne =[ { "Id": 1, "Name": "ONLINE", "Status": false, "Track": false, }, { "Id": 2, "Name": " ...

Express throwing module errors

I encountered an issue while attempting to expose a REST service in an electron app using expressJS. Following a tutorial, I added express and @types/express to the project. However, when trying to implement a "get" method and running the build with ng bui ...

Prevent redundancy by caching svg icons to minimize repeated requests

I have a collection of info cards on my page, each featuring its own unique illustration along with a set of common icons (SVG) for options such as edit, delete, and more. While the illustrations vary from card to card, the icons remain consistent across a ...

Function that yields promise result

I need help figuring out how to make this recursive function return a promise value. I've attempted various approaches, but they all resulted in the search variable ending up as undefined. public search(message: Message) { let searchResult: strin ...