Navigating through Interfaces in Typescript with Union Types

When faced with a scenario where you have two interfaces that are similar enough to be processed by the same logic, what is the most effective approach to take:

interface DescriptionItem {
    Description: string;
    Code: string;
}
interface NamedItem {
    Name: string;
    Code: string;
}

function MyLogic(i: DescriptionItem | NamedItem) {
    var desc = (<DescriptionItem>i).Description || (<NamedItem>i).Name;

    return i.Code + ' - ' + desc;
}

Although the current method works, I am interested in enhancing the var desc = ... line. Is there a more optimal solution for handling this scenario in TypeScript?

Answer №1

When it comes to TypeScript, interfaces are only available during compile-time, making it challenging to test for interface types during run-time. Your approach in the provided code seems sensible and is likely the best option in this scenario.

Nevertheless, if you have the freedom to transform your interfaces into classes, TypeScript's type guards can offer a more refined way of conducting type checks:

class DescriptionItem {
    Description: string;
    Code: string;
}
class NamedItem {
    Name: string;
    Code: string;
}

function MyLogic(i: DescriptionItem | NamedItem) {
    let desc: string;
    if (i instanceof DescriptionItem) {
        desc = i.Description;
    } else {
        desc = i.Name;
    }

    return i.Code + ' - ' + desc;
}

Answer №2

Although this question has been around for a while, I recently delved into the same issue while learning about the distinctions between | and & in creating Type Unions.

There are various ways to address this dilemma (while also keeping it linter-friendly). The most effective approach is to employ a discriminator in all your interfaces (referred to as a narrow interface).

//Begin by crafting a super-interface with the discriminator
interface B
{
    kind: 'b1' | 'b2' //define the discriminator with all possible values as string literals (this is where the magic lies)
}

interface B1 extends B
{
    kind: 'b1' //narrow down the literals inherited by the interfaces to a single value
    //then include your interface-specific fields
    data1: string;
    data: string;
}

interface B2 extends B
{
    kind: 'b2' //narrow down the literals inherited by the interfaces to a single value
    //then include your interface-specific fields
    data2: string;
    data: string;
}

//Create a B1 type instance using the literal value 'b1' from the B1 interface
var b1: B1 | B2 = {
    kind: 'b1',
    data: 'Hello From Data',
    data1: 'Hello From Data1'
    //typescript will prevent setting data2 as this represents a B1 interface
}
//And a B2 Type with the kind 'b2'
var b2: B1 | B2 = {
    kind: 'b2',
    data: 'Hello From Data',
    //typescript will prevent setting data1 as this represents a B2 interface
    data2: 'Hello From Data2'
}

Another option is to verify fields on an object using the "in"-keyword, but this may lead to extensive boilerplate code, requiring updates every time the interface changes.

interface B1
{
    data1: string;
    data: string;
}

interface B2
{
    data2: string;
    data: string;
}

var b3: B1 | B2 = {
    data: 'Hello From Data',
    data1: 'Hello From Data1',
    data2: 'Hello From Data2'
}

console.log(b3.data); //this field is common in both interfaces and does not require a check

if ('data1' in b3) //check if 'data1' exists in the object
{
    console.log(b3.data1);
}
if ('data2' in b3) {
    console.log(b3.data2); //check if 'data2' exists in the object
}

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

Using Node.js with Typescript and RedisJSON allows for a powerful and efficient

I've recently started delving into nodejs, typescript, and redis for programming. However, I've encountered an issue with redis: when defining a data interface to be stored in redis, the system throws errors as soon as I try to specify the data t ...

RXJS expand keeps on recursing indefinitely

After successfully uploading a file to Firebase, I implemented a recursive function to listen for GCP trigger logs. Everything seems to be working well, but I'm facing an issue where the recursive expand function never exits. Despite checking the val ...

Troubleshooting problem in Grunt-TS, Grunt, watch/compile, and Dropbox integration

UPDATE: Recently, I've been experiencing some issues with another computer when it comes to watching and compiling. It seems like the tscommandxxxxxx.tmp.txt files that are generated during compilation might be causing the trouble... sometimes they ar ...

What causes an inference site to have varying effects when accessed directly versus when it is retrieved from a function?

Below is the provided code snippet : declare class BaseClass<TValue = any> { value: TValue; foo(value: TValue): void; } type Wrapped<T> = { value: T } declare class ConcreteClasss<TValue> extends BaseClass<TValue> { construc ...

Is the Cyrillic encoding feature not functioning properly in Angular 4 with .Net Core 2?

Struggling with handling Cyrillic characters in my current project. Utilizing .Net Core 2 along with Angular 4.2.5 I've noticed that displaying a string in the templates using {{ someCyrillicString }} works fine. However, encountering issues when tryi ...

Anticipating the completion of multiple observable subscription functions

Is there a way to replace and convert all words in an array using an object's method that returns an observable? I found a helpful solution on this post which uses bind to pass the correct value. After all subscriptions are complete, I want to execut ...

Utilizing React Typescript Discriminating Unions to choose between two different types based solely on props

In my project, I have a component that consists of different types: type Base = { color: string } type Button = { to: string } & Base type Link = { link: string linkNewTab: boolean } & Base type ComponentProps = Button | Link e ...

Vue.js 3 with TypeScript is throwing an error: "Module 'xxxxxx' cannot be located, or its corresponding type declarations are missing."

I developed a pagination plugin using Vue JS 2, but encountered an error when trying to integrate it into a project that uses Vue 3 with TypeScript. The error message displayed is 'Cannot find module 'l-pagination' or its corresponding type ...

There is no way to convert a strongly typed object into an observable object using MobX

I have been utilizing the MobX library in conjunction with ReactJS, and it has integrated quite smoothly. Currently, I am working with an observable array structured as follows: @observable items = []; When I add an object in the following manner, everyt ...

Is it possible to easily organize a TypeScript dictionary in a straightforward manner?

My typescript dictionary is filled with code. var dictionaryOfScores: {[id: string]: number } = {}; Now that it's populated, I want to sort it based on the value (number). Since the dictionary could be quite large, I'm looking for an in-place ...

Performing calculations on two properties of an observable object in Angular 8 and then storing the result in a new property

Looking for guidance on how to display the sum of two properties from an observable data. Take a look at the code below and let me know your thoughts: Typescript class export class Amount { Amount1: number; Amount2: number; Total:number; } In typescript ...

Struggling with testing the checkbox when it changes inside the CardHeader Avatar={} component

I've recently implemented a feature similar to the example showcased on MaterialUI's TransferList. However, I'm encountering difficulties accessing a checkbox within the avatar={}. The project utilizes Jest and Enzyme for testing purposes. T ...

establishing the default value as p-multiselect

Here is the code snippet I am currently working on: export class LkBoardStatus { id : number = 0; descr : string = ''; } In the component.ts file, I have defined the following: //... lkBoardStatusList: LkBoardStatus[] = []; selectedStat ...

Exploring the difference between loop and stream patterns in Azure Service Bus message receiving operations

I am currently setting up the Azure Service Bus messaging infrastructure for my team, and I am working on establishing best practices for developing Service Bus message receivers. We are in the process of creating a new service to consume the Service Bus m ...

Automatically adjust the model input (Signal) based on the parent and respond to any alterations in the children

Within my Angular 16 application, I have a parent component that passes a plain JavaScript object (myObj) to a child component, where it is treated as a model<MyObj>. <!-- parent.component.html --> <app-children [myObjModel]="myObj&qu ...

Creating a modal dialog using a function in a TypeScript file

Hey there, fellow developers! I have a question that might seem simple. So, in my HTML code I've got a Modal Dialog that pops up using ng2 Bootstrap. It's working fine, but... I want to replace this line of code "<button class="btn btn-prim ...

Solving the error message "Cannot find module '@angular/router/src/utils/collection' or its corresponding type declaration"

How do I troubleshoot this Error: src/app/metronic/orderByLocation/locationsByOneOrder/locationsByOneOrder.component.ts:7:25 - error TS2307: Cannot find module '@angular/router/src/utils/collection' or its corresponding type declarations.m 7 imp ...

Enhancements to a NativeScript Application

After running some tests on my NativeScript app following the steps outlined here - , I found that it takes 18 seconds for the program to start and for a user to log in. Is this considered acceptable performance? Appreciate any feedback provided! ...

What is the best way to transform a synchronous function call into an observable?

Is there a conventional method or developer in RxJS 6 library that can transform a function call into an observable, as shown below? const liftFun = fun => { try { return of(fun()) } catch (err) { return throwError(err) } ...

"Discover the power of Next.js by utilizing dynamic routes from a curated list

I am working on a Next.js application that has a specific pages structure. My goal is to add a prefix to all routes, with the condition that the prefix must be either 'A', 'B', or 'C'. If any other prefix is used, it should re ...