Creating a general function to accommodate two alike types

Is there a way to modify the addSuffix function to handle two different types and return them simultaneously?

Here's an example:

   type First = {
        name: string,
        address: string,  
    }
    
    type Second = {
        name: string,
        email: string,
    }
    
    type Third = First | Second;
    
    function addSuffix(payload: Third): Third {
        payload.name += "suffix";
        return payload;
    }
    
    const a: First = {
        "name": "John",
        "address": "London",
    }
    
    const b: Second = {
        "name": "Alice",
        "email": "example@example.com"
    }
    
    function doA(payload: First): First {
        // some operations
        return addSuffix(payload);
    }
    
    function doB(payload: Second): Second {
        // some operations
        return addSuffix(payload);
    }
    
    doA(a);
    doB(b);

An issue arises:

'Third' is not compatible with 'First'. 'address' is required in 'First' but missing in 'Second'.

How can the addSuffix function be modified to resolve this issue? In traditional OOP languages, interfaces can be used (interface with name property), but how can it be achieved in TypeScript?

Answer №1

The main issue here is that the function addSuffix does not clearly indicate that it will always return the same type that is passed in. As a result, functions doA and doB cannot guarantee the type of object they will receive as output.

In object-oriented programming languages, one would typically use interfaces (with a name property) to address this issue. But how can this be achieved in TypeScript?

Thankfully, TypeScript provides interfaces for this purpose:

interface Nameable {
    name: string;
}

function addSuffix<T extends Nameable>(payload: T): T {
    payload.name += "suffix";
    return payload;
}

There is no need to manually implement the Nameable interface for objects like First and Second, as it is inferred from the presence of the name property in both.

Additionally, you can use a type alias instead of an interface to achieve the same goal, but with slightly different syntax:

type Nameable = {
    name: string
}

function addSuffix<T extends Nameable>(payload: T): T {
    payload.name += "suffix";
    return payload;
}

Alternatively, you can define the type directly in the function signature:

function addSuffix<T extends { name: string }>(payload: T): T {
    payload.name += "suffix";
    return payload;
}

Answer №2

In my opinion, incorporating an intersection type is essential:

type Third = First & Second;

By implementing the intersection type in the addSuffix method, the resulting third type becomes compatible with the desired first or second type due to its inclusion of the required properties.

An alternative and more organized approach involves utilizing an interface for extension:

interface INamed {
    name: string,
}

type Second = INamed & {
    email: string,
}

function addSuffix<T extends INamed>(payload: T): T {
  ...

For more details, refer to the documentation: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#intersection-types

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

Guide for inserting personalized buttons onto a map with the Bing Maps 6.3 Ajax Control

Looking to enhance a Bing Map with custom buttons for more interactive functionality? I'm interested in creating a custom dashboard on the map that would allow users to toggle different information or colors displayed on specific pins or polygons by s ...

Specific generic types do not incorporate abstract type context

Take a look at this code snippet: type Data = { person: { id: number; name: string; age: number } item: { id: number; name: string; price: number } transaction: { id: number; personId: number; itemId: number; quantity: number } } type Action<T ex ...

The TransferList component in Material UI does not update its state based on props when using the useState

My TransferList component in Material UI receives an array of previously selected items as props.selectedItems. The props.Items contains all available items. I expected to see props.selectedItems in the left panel of TransferList, but the left panel is em ...

The action 'addSection' is restricted to private access and can only be utilized internally by the class named 'File'

const versionList = Object.keys(releaseNote) for (const key of versionList) { // Skip a version if none of its release notes are chosen for cherry-picking. const shouldInclude = cherryPick[key].some(Boolean) if (!shouldInclude) { continue } // Include vers ...

Troubleshooting a Missing Angular (8) Pipe Error in Your Ionic 4 Application

Despite seeing similar questions posted here, none have provided a solution to my issue. I believe I am implementing it correctly, but clearly something is not right. In the app I'm developing with Ionic 4, I need to add a key to a URL in a gallery. ...

Using React-Testing-Library to Jest TestBed Hook in TypeScript for Jest Testing

I'm currently facing a challenge while attempting to integrate the react-hooks library with Formik, specifically using useFormikContext<FormTypeFields>() in TypeScript within my project. I have certain fields where I want to test the automation ...

"Combining background images with javascript can result in displaying visual elements

Hello! I am in need of assistance with a CSS + Javascript fog effect that I have developed. It is functioning properly on Firefox, Opera, and Chrome but encountering issues on IE and Edge browsers. The effect involves moving two background images within a ...

The Node Express.js app is functioning properly when run locally, but displays the error "Cannot GET /" when running in a Docker container

My Node application has an Express.js server defined like this: const express = require('express') const SignRequest = require('./SignRequest/lambda/index.js') const VerifyResponse = require('./VerifyResponse/lambda/index.js') ...

I seem to be having trouble locating the correct file location

Apologies for the simplicity of my question. I've been struggling to include the find.js file in my articles.js file. Despite trying multiple variations, I can't seem to get the pathname right and haven't found a solution online. Can someon ...

Setting up and launching a fresh JS project

Hey there, I recently began a course on Udemy to dive into the world of JavaScript and React. However, after installing Node.js and NPM, I encountered an issue when trying to use npm start. The error message reads "ENOENT: no such file or directory." I&apo ...

Issue with Jquery animation persists: mouse cursor does not change and style remains unchanged even after clicking

I have encountered an issue with JQuery animation on Chrome. According to the requirements, I need to animate a div when a link is clicked. When the cursor hovers over the link, it should be underlined and change to a pointer. However, even after clickin ...

Is it possible to showcase D3 charts on an .epub file?

For my research project, I am exploring the possibilities of .epub files and experimenting with embedding JavaScript code to display data visualizations. I am currently using calibre to convert an HTML file containing D3 scatterplots into an .epub. The s ...

Exploring the combination of Express router, Auth0, and plain Javascript: A guide to implementing post-login authentication on a router

After creating a front end with vite using vanilla javascript and setting up a backend with node.js express routes, I successfully integrated swagger for testing purposes. I have managed to enable functionalities such as logging in, logging out, and securi ...

Tips for deactivating dates in the jquery datepicker that correspond to entries in a MySQL database

Is there a way to disable dates directly from a MySQL database in jQuery Datepicker? It seems tedious to constantly update dates in JavaScript, so I'm exploring the option of storing future disable dates in the MySQL database and displaying them in th ...

Develop a versatile JavaScript or jQuery script that automatically sets the focus on the first input field within a div or tag when the user clicks on it

I am currently working on creating a data entry form using a table layout. The form has two columns - the first column for input titles and the second column mostly for input tags. I styled the inputs in the second column to appear transparent with no bord ...

Pass the initial value from a parent component to a child component in ReactJS without the need for state management

Initially, I have a parent Component A that calculates an initial value for its child Component B. The initial value is computed in the componentDidMount() of Component A and passed to B using props: <ComponentB initialValue={this.state.value} handleCha ...

Personalized Dropdown Menus for Internet Explorer 8

Seeking recommendations for stylish custom select boxes that are compatible with IE8 and function flawlessly. Many of the custom scripts I've come across perform admirably, but tend to suffer setbacks when it comes to working smoothly in IE8. ...

What is the process for connecting personalized toggle controls to a checkbox input field?

I have created a custom slide toggle control to enhance the functionality of my checkboxes in the app. You can check out the controls by following this link: http://codepen.io/spstratis/pen/fJvoH Currently, I am looking for a way to connect these switche ...

After manipulating the array, Vue fails to render the input fields generated by the v-for directive

After setting the value externally, Vue component won't re-render array items. The state changes but v-for element does not reflect these changes. I have a component that displays items from an array. There are buttons to adjust the array length - &a ...

Using the .val method on an input element modifies the HTML content without affecting the value displayed

Currently, I am working on validating the range input type by utilizing JavaScript along with jQuery's val method. While it successfully displays the output in HTML, I encountered an issue where changes to the value are not being logged in the console ...