Is there a way in Typescript to determine the type of a union type when a specific condition is satisfied?

I am faced with a scenario where I have an object that can take on two different shapes:

{ageTop:42}

or

{locations: [{name:Myrtle Beach}]}

These objects are passed as parameters to a function, and I want to ensure that the function only receives the types it can work with. It could be one of these objects, both, or none.

To address this, I have devised a solution by defining interfaces and types as follows:

interface locationsType {
 locations: Array<{name: string}>
}

interface ageTopType {
ageTop: number
}

type keyType = 'ageTop' | 'locations'

I aim to enforce the correct usage of interfaces based on the selected key value by using conventions in my code. However, I am concerned about potential errors that may arise if these conventions are not adhered to. How can I reinforce this through typing? Essentially, I want to implement a simple logic that looks like this:

 interface traitType {
   key: keyType
   value: this.key === 'locations' ? locationsType : ageTopType;
}

class Developer {
  locations: []
  ageTop;
  constructor(ageTop,locations) {
  this.ageTop = ageTop
  locations.forEach(_val => this.locations.push(_val))
  }
  loadTrait(trait:TraitType) {
    if(trait.key === location) {
      this.trait.value.forEach(_val => this.locations.push(_val)
      return
    }
  this[trait.key] = trait.value;
 }
}

I have attempted the above approach but encountered some issues. Any suggestions or insights?

Answer ā„–1

If you want to accomplish this task in a traditional way, you can use a concept called a discriminated union. In a discriminated union, the members share a common property, known as the discriminant, which helps in narrowing down a value of the union type to one of its members. For instance:

interface LocationsType {
    locations: Array<{ name: string }>
}

interface AgeTopType {
    ageTop: number
}

You can define TraitType as a discriminated union like this:

type TraitType = 
  { key: 'ageTop', val: AgeTopType } | 
  { key: 'locations', val: LocationsType };

The compiler will utilize control flow analysis to refine the type of TraitType based on the discriminant test, as demonstrated in the following example:

function foo(t: TraitType) {
    if (t.key === "ageTop") {
        t.val.ageTop.toFixed();
    } else {
        t.val.locations.map(x => x.name.toUpperCase());
    }
}

I hope this explanation clarifies things for you. Good luck with your implementation!

Here's a link to the code playground

Answer ā„–2

One approach could be to combine different types in a union

interface locationsType {
    locations: Array<{name: string}>;
}

interface ageTopType {
    ageTop: number;
}

type keyType = 'ageTop' | 'locations';

enum traitTypes {
    location = 'locations',
    ageTop = 'ageTop',
}

type traitType = {
    key: traitTypes.location;
    value: Array<locationsType>;
} | {
    key: traitTypes.ageTop;
    value: ageTopType;
};

class Developer {
    public locations: Array<locationsType> = [];
    public ageTop: ageTopType;

    constructor(ageTop: ageTopType, locations: Array<locationsType>) {
        this.ageTop = ageTop;
        this.locations.push(...locations);
    }

    public loadTrait(trait: traitType): void {
        if (trait.key === traitTypes.location) {
            this[trait.key].push(...trait.value);
        } else if (trait.key === traitTypes.ageTop) {
            this[trait.key] = trait.value;
        }
    }
}

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

What is the method for importing the "numeric" library version "^1.2.6" into an Angular project?

I recently added the package "numeric": "^1.2.6" to my project. In addition, I included the types: "@types/numeric": "^1.2.1" When attempting to import this into my Angular application: import * as numeric from &ap ...

Ways to effectively implement a function type specified in an interface

Consider the following interface: interface MyProps { onEvent: (name: string, data: any) => void; } Is there a way to utilize the function type in order to avoid unused parameter errors during compilation? eventHandler = (name: string, data: any) = ...

Looking to conceal the input div without compromising the functionality in Angular 14

Here is the provided HTML code for scanning a barcoder and assigning it to the barCodeNumber variable. The onChange() function will be called once the barcode is scanned. Question: How can I hide the div on the UI while still allowing the function to work ...

Setting up next-i18next with NextJS and Typescript

While using the next-i18next library in a NextJS and Typescript project, I came across an issue mentioned at the end of this post. Can anyone provide guidance on how to resolve it? I have shared the code snippets from the files where I have implemented the ...

Exploring the concept of inheritance and nested views within AngularJS

I've encountered a challenge while setting up nested views in AngularJS. Utilizing the ui-router library has been beneficial, but I'm facing issues with separate controllers for each view without proper inheritance between them. This results in h ...

Enumerated types in Typescript: access the values

Below is a flagged enum I have: enum PermissionEnum { SU = 1 << 0, // 1 Administrator = 1 << 1, // 2 User = 1 << 2 // 4 } If the value given is 6, how can I achieve: An array of strings -> ['Adm ...

Customizing the initial page layout in Elm

I am new to Elm and I need help with a particular issue. Can someone provide guidance or direct me to a useful resource for solving this problem? The challenge Iā€™m facing involves editing the start page of a website by removing specific elements, as list ...

RTK update mutation: updating data efficiently without the need to refresh the page

I am facing an issue with my mui rating component in a post-rating scenario. Although the rating updates successfully in the data, the page does not refresh after a click event, and hence, the rating remains enabled. To address this, I have implemented a d ...

Restricting the number of mat-chips in Angular and preventing the input from being disabled

Here is my recreation of a small portion of my project on StackBlitz. I am encountering 4 issues in this snippet: I aim to restrict the user to only one mat-chip. I attempted using [disabled]="selectedOption >=1", but I do not want to disable ...

The sequence of events in React Native following a navigation.navigate call

Seeking suggestions and advice, I currently have the following 2 lines of code within a react native expo component: this.props.navigation.navigate("App"); patchUser(this.state.dataSource.userInfo.username, this.state.dataSource.userInfo.firstN ...

The NextJS application briefly displays a restricted route component

I need to ensure that all routes containing 'dashboard' in the URL are protected globally. Currently, when I enter '/dashboard', the components display for about a second before redirecting to /login Is there a way to redirect users to ...

``There seems to be an issue with the Deno logger FileHandler as it

I am currently in the process of setting up loggers for an application. I have a handler named console which logs every event to the console. Additionally, there is a handler called app that is supposed to log all events to a file. While the logs are succ ...

Utilize @db.Decimal within the Prisma framework for the parameters "s", "e", and "d"

When I define the schema in Prisma like this: value Decimal? @db.Decimal(7, 4) Why do I always get this format when retrieving the value from the database: "value": { "s": 1, "e": 0, & ...

Webpack 2.7.0 throws an error: "Unexpected parameter: theme"

At the moment, I am on webpack 1.16.0 and using --theme as an argument to set the output path and plugin paths. The command appears as: rimraf dist && webpack --bail --progress --profile --theme=<name of theme> However, as I try to upgrade ...

Associate a unique identifier string with a randomly generated integer identifier by Agora

For my current web project, I am utilizing a String username as the UID to connect to the channel in an Agora video call. However, I now need to incorporate individual cloud recording by Agora into the project. The challenge lies in the fact that cloud r ...

Display the chosen HTML template in real-time

Recently, I started using Angular 2 and encountered an issue that I need help with. 1] I have created 2-3 templates for emails and SMS that will display predefined data. 2] I have also designed a screen with a dropdown menu containing options like email ...

Struggling to access the properties of a Material-UI Button

import * as React from "react"; import { styled } from "@mui/material/styles"; import MuiButton from "@mui/material/Button"; import Slider from "@mui/material/Slider"; interface Props { type: "primary" | ...

Steps for transitioning from a mapped type to a discriminated union

Forgive me if this question has already been posed. I made an effort to search for a solution, but it seems I may not be using the correct terms. The issue arises with this particular structure. It involves a simple mapped type: type Mapped = { squ ...

What is the best way to determine which variable holds the greatest value in Angular?

I am working with different array lengths stored in variables and trying to determine which variable is the greatest, then change the font color of that variable. However, I encountered an issue where if two variables have the same value, only one is being ...

Experiencing problems with React createContext in Typescript?

I've encountered a strange issue with React Context and Typescript that I can't seem to figure out. Check out the working example here In the provided example, everything seems to be working as intended with managing state using the useContext ...