Exploring Objects with Union Types in TypeScript

Looking to iterate through an object that combines two interfaces.

    interface Family {
      cat: string;
      age: string;
      family: string;
      lastYearFamily: string;
    }
    
    interface Model {
      cat: string;
      age: string;
      model: string;
      lastYearModel: string;
    }
    
    interface Props {
        attributionType: 'family' | 'model';
        attributions?: Family[] | Model[];
    }
    
    const RowComponent = ({
    attributionType,
    attributions
    }: props) =>{
    
       return (
          {attributions && attributions.map((attribution: Family | Model ) => (
                             <tr>
                                {
                                    attributionType === 'family' && (
                                        <React.Fragment>
                                            <th>{attribution.family}</th>
                                            <th>Family</th>
                                        </React.Fragment>
                                    )
                                } 
                                {
                                    attributionType === 'model' && (
                                        <React.Fragment>
                                            <th>{attribution.model}</th>
                                            <th>Model</th>
                                        </React.Fragment>
                                    )
                                } 
                             </tr>
                        ))}
    );
    
    }

Struggling with accessing non-common members in the union object.

Can only access cat and age, not family, lastYearFamily, etc.

Prefer to keep code generic without separate components for each attribution type.

Answer №1

When defining the string 'family' in the Props interface, it is important to explicitly specify that it should infer to Family[] for the attributions. Otherwise, there may be confusion regarding the correlation between attributionType and attributions.

    // your code
    interface Props {
        attributionType: 'family' | 'model';
        attributions?: Family[] | Model[];
    }

To provide clear guidance to the compiler, it is recommended to define separate interfaces for each type and then combine them accordingly:

interface FamilyProp {
  attributionType: "family";
  attributions?: Family[];
}

interface ModelProp {
  attributionType: "model";
  attributions?: Model[];
}

type Props = ModelProp | FamilyProp;

function foo(bar: Props) {
    if (bar.attributionType === "model") {
        bar.attributions[0].lastYearModel; // compiles fine
    }
    if (bar.attributionType === "family") {
        bar.attributions[0].lastYearFamily; // compiles fine
    }
}

By using Type Guard, the compiler will properly infer the types within the if statements.

Answer №2

To implement this functionality, one would typically utilize custom type guards. These guards involve converting a variable to a potential type and verifying the presence of a specific field unique to that type. This process allows for the inference that the value stored in the variable is indeed of the specified type.

interface Family {
      cat: string;
      age: string;
      family: string;
      lastYearFamily: string;
}
    
interface Model {
      cat: string;
      age: string;
      model: string;
      lastYearModel: string;
}

const isFamily = (f: Family|Model): f is Family => {
    return (f as Family).family !== undefined
}

const isModel = (f: Family|Model): f is Model => {
    return (f as Model).model !== undefined
}

const x : (Family | Model)[] = [
    {cat: "x", age: "y", family: "z", lastYearFamily: "w"},
    {cat: "x", age: "y", model: "z", lastYearModel: "w"}
]

x.map(e => {
    if (isFamily(e)) {
        return e.family;
    } else {
        return e.model;
    }
})

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

Importing configuration file in CRA Typescript with values for post-deployment modifications

I am currently working on a React app that utilizes Create React App and Typescript. My goal is to read in configuration values, such as API URLs. I have a config.json file containing this data, here's a sample snippet with placeholder information: { ...

Displaying data from an Angular subscription in a user interface form

I am attempting to transfer these item details to a form, but I keep encountering undefined values for this.itemDetails.item1Qty, etc. My goal is to display them in the Form UI. this.wareHouseGroup = this.formBuilder.group({ id: this.formBuilder.contr ...

Identifying the specific type within a union of types using a discriminator

How can I specify the correct typing for the action argument in the function withoutSwitchReducer as shown below? enum ActionTypesEnum { FOO = 'FOO', BAR = 'BAR', } type ActionTypes = { type: ActionTypesEnum.FOO, paylo ...

Transforming button click from EventEmitter to RXJS observable

This is the functionality of the component utilizing EventEmitter: import { Component, Output, EventEmitter } from "@angular/core"; @Component({ selector: "app-my-component", template: ` <button (click)="clickEvent($event)& ...

Express Server Providers for Angular 17's Server-Side Rendering

I attempted to share my request and response object with the Angular application by defining Providers in the "server.ts" file. However, when injecting them into app.component, they always appear undefined regardless of whether I am in the server or clie ...

Creating Beautiful MDX Layouts with Chakra UI

Currently, I am attempting to customize markdown files using Chakra UI within a next.js application. To achieve this, I have crafted the subsequent MDXComponents.tsx file: import { chakra } from "@chakra-ui/react" const MDXComponents = { p: (p ...

Stepper that is vertical combined with table information

I am currently facing a unique challenge with a component I'm trying to create. It's a combination of a vertical Stepper and a Datagrid. My goal is to group specific table sections within the content of a vertical Stepper, purely for data visual ...

Tips for specifying the return type of app.mount()

Can I specify the return value type of app.mount()? I have a component and I want to create Multiple application instances. However, when I try to use the return value of mount() to update variables associated with the component, TypeScript shows an error ...

What could be causing the issue with Vite build and npm serve not functioning together?

After shifting from CRA to VITE, I am encountering a problem with serving my app. I successfully build my app using vite build. and can serve it using Vite serve without any issues. However, I want to use npm's serve command. Whenever I run vite bui ...

The resend email feature isn't functioning properly on the production environment with next js, however, it works seamlessly in the development environment

import { EmailTemplate } from "@/components/email-template"; import { Resend } from "resend"; const resend = new Resend("myApiKey"); // this works only in dev // const resend = new Resend(process.env.NEXT_PUBLIC_RESEND_API_KE ...

Guide on integrating google play services into a nativescript plugin seed?

I am developing a plugin for NativeScript using the recommended nativescript-plugin-seed available at this link. In my plugin, I require access to the Google Location service, but I am facing issues with accessing it. In order to implement the required de ...

The Angular custom modal service is malfunctioning as the component is not getting the necessary updates

As I develop a service in Angular to display components inside a modal, I have encountered an issue. After injecting the component content into the modal and adding it to the page's HTML, the functionality within the component seems to be affected. F ...

Issue with exclude not functioning in tsconfig.json for Angular Typescript deployment

I am encountering an issue with a module within the node_modules directory while compiling my Angular 4 project. The error messages I'm receiving are as follows, even after attempting to exclude the problematic module in the tsconfig.json file. Can an ...

One-Of-A-Kind Typescript Singleton Featuring the Execute Method

Is it feasible to create a singleton or regular instance that requires calling a specific method? For instance: logger.instance().setup({ logs: true }); OR new logger(); logger.setup({ logs: true }); If attempting to call the logger without chaining the ...

Difficulty with Angular's Interpolation and incorporating elements

I've encountered an issue with String Interpolation while following an Angular course. In my server.component.ts file, I've implemented the same code as shown by the teacher in the course: import { Component } from "@angular/core"; @Component ( ...

Distinguishing Between TypeScript Interface Function Properties

Could anyone clarify why the assignment to InterfaceA constant is successful while the assignment to InterfaceB constant results in an error? interface InterfaceA { doSomething (data: object): boolean; } interface InterfaceB { doSomething: (data: obje ...

Deactivate the button if the mat-radio element is not selected

Here is my setup with a mat-radio-group and a button: <form action=""> <mat-radio-group aria-label="Select an option"> <mat-radio-button value="1">Option 1</mat-radio-button> <mat-radio-b ...

Using Rxjs to dynamically map values from an array with forkJoin

Greetings! I have a collection of Boolean observables and would like to apply a logical AND operation. Currently, I am passing static values 'a' and 'b', but I am unsure of the number of elements in the totalKeys array. import { forkJoi ...

What is the proper way to enhance properties?

In the process of developing a Vue3 app using Typescript, one of the components is designed to receive data through props. Initially, everything functioned smoothly with the basic setup: props: { when: String, data: Object }, However, I de ...

Developing a declaration for an unnamed function in a JavaScript file

module.exports = function (argument1, argument2) { return { myFunction } function myFunction () { ... } } What is the process for creating a TypeScript declaration file for this specific JavaScript file? ...