Asserting types for promises with more than one possible return value

Struggling with type assertions when dealing with multiple promise return types? Check out this simplified code snippet:

interface SimpleResponseType {
   key1: string
};

interface SimpleResponseType2 {
   property1: string
   property2: number 
};

interface ServiceType {
   fetchResponse: () => Promise<SimpleResponseType | SimpleResponseType2> 
};

class Service implements ServiceType {
   async fetchResponse() {
      // fetching api data and returning json here
      return { key1: 'json' };
   };
};

const service = new Service();

await service.fetchResponse().then(resp => {
   // Expecting typeof SimpleResponseType
   console.log(resp.key1)
})

An error is thrown by TypeScript on the last key1 line:

Property 'key1' does not exist on type 'SimpleResponseType2'.ts(2339)

Attempting to assert the type results in more errors. For instance, trying this assertion:

await service.fetchResponse().then((resp as SimpleResponseType) => {

Leads to this error:

Argument of type 'SimpleResponseType' is not assignable to parameter of type '(value: SimpleResponseType | SimpleResponseType2) => SimpleResponseType | SimpleResponseType2 | PromiseLike<...>'.
Type 'SimpleResponseType' provides no match for the signature '(value: SimpleResponseType | SimpleResponseType2): SimpleResponseType | SimpleResponseType2

The only working assertion found is:

const respTyped = resp as SimpleResponseType

Using respTyped instead of resp.

Perhaps there's a more effective approach to tackle this issue?

Answer №1

Uncertainty surrounds your desired outcome, but it seems like you're striving to achieve a specific objective. It involves strongly typing your Service type for precise response handling.

interface SimpleResponseType {
   key1: string
};

interface SimpleResponseType2 {
   property1: string
   property2: number 
};

interface ServiceType<T extends SimpleResponseType | SimpleResponseType2> {
   fetchResponse: () => Promise<T> 
};

class Service implements ServiceType<SimpleResponseType> {
   async fetchResponse() {
      // code here to get API data and return JSON
      // returning a sample response 
      return { key1: 'json' };
   };
};

const service = new Service();
export {}
await service.fetchResponse().then(resp => {
   // Expecting the type of response to be SimpleResponseType
   console.log(resp.key1)
})

Answer №2

If you're considering handling different return types for the same function, you might want to explore using overloads. However, be prepared to define unique input parameters for each overload. It's unusual to have multiple return types for a single API/function call unless there are specific reasons in your scenario.

Alternatively: Here's a suggested approach if you aim to utilize the same function across diverse API routes.

Check out TS playground for implementation

interface SimpleResponseType {
   key1: string
};
interface SimpleResponseType2 {
   property1: string
   property2: number 
};
type SimpleResponseType3 = Array<SimpleResponseType2>;

enum Routes {
    Route1 = "/api/route1",
    Route2 = "/api/route2",
    RouteWParam = "/api/route2/:id"
}

class Service {

    async fetchResponse(route: Routes.Route1) : Promise<SimpleResponseType>;
    async fetchResponse(route: Routes.Route2) : Promise<SimpleResponseType2>;
    async fetchResponse(route: Routes.RouteWParam, params: { id: number }) : Promise<SimpleResponseType3>;
    async fetchResponse(route: Routes, params?: Record<string, string | number>) {
        let url : string = route;
        if (params !== undefined) {
            //replace all params with the params passed
            url = Object.entries(params).reduce((previousValue: string, [param, value]) => {
                return previousValue.replace(`:${param}`, '' + value)
            }, route);
        }
        // You do lose type-safety inside this function. TS doesn't care if route is `Route1` and you try to return `SimpleResponseType2`.
        // That's up to you to make sure.
        const data = { key1: 'sdf' }
        const data2 = { property1: 'val', property2: 345 };
        
        return new Promise((res) => {
            setTimeout(() => res(data2), 100)
        })
    };
};

Answer №3

const data = { name1: 'apple' };

It seems like you're attempting something unusual. What if you try this instead:

const data = { name1 - 'apple' };

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

Why is it that in React the render method is automatically bound to the component instance, while custom methods are not provided

Why is the render method automatically bound to the component instance in a class, but custom methods such as event handlers are not? I realize that using the bind keyword can make these event handlers work, but I'm curious to know why "this" can be ...

Troubleshooting ng-repeat in AngularJS: Multi checkbox filter malfunction

My AngularJS app has multiple age range filters implemented, but one of them is not functioning correctly and I can't figure out why. You can view a functional example here. The various filters are defined as follows: .filter('five', func ...

The option to "open in new tab" is absent from the right-click menu when clicking a link on a website

Why does the option to open a link in a new tab not appear when using JavaScript or jQuery, but it works with an anchor tag? I have tried using window.location and window.open, as well as adding the onclick attribute to a div, but the option still doesn&ap ...

Is it possible to modify the appearance or behavior of a button in a React application based on its current state?

I'm looking to customize the color of a button based on different states. For example, if the state is active, the button should appear in red; otherwise it should be blue. Can anyone suggest how this can be achieved in React? Furthermore, I would als ...

Are there any instances of a race condition present in the following?

In the express server code snippet provided, there is a common object that is being manipulated in three different RESTful endpoints. Given that all HTTP requests in Node.js are asynchronous, it's possible to have simultaneous PUT and GET requests occ ...

React components can be used to dynamically render and display an array of objects through methods like reduce and

Here's the scenario at hand: (https://codesandbox.io/s/8p21n6p09l) I have an array of objects (referred to as modules) structured like this: const modules = [ { thematicArea: "Topic 1", id: 1, name: "Building assertive attitude", d ...

The output from the Angular .then function is not showing up on the webpage

Within my stucontrollers.j, I have the following code: /// <reference path="../angular.js" /> var stucontrollers = angular.module("stucontrollers", []); stucontrollers.controller("GetStudentsList", function GetStudentsList($scope, $http) { $ ...

Challenges encountered with input outcomes

I am facing an issue with input results. I have a button that triggers a function to check for empty input fields. However, when I click the button, it always falls into the last if statement and displays as if the fields are not empty. I have already att ...

retrieve information at varying intervals through ajax

In my web page, there are two div elements that both fetch server data using AJAX. However, div-a retrieves data every second while div-b retrieves data every minute. How can I adjust the frequency at which each div fetches server data? ...

The data received from the frontend is being replicated in the backend, causing duplication issues in a React

Whenever I click the button labeled onClick, it triggers the transmission of data (both time and ID) to the backend. The issue at hand is that the backend seems to be receiving the data twice instead of just once. On inspecting req.body, it becomes eviden ...

What is the function of '@' symbol in coding?... obtain {ModuleName} from '@ModuleName'

What is the significance of the '@' symbol in imports? I've noticed that various modules like '@react-navigation', '@babel', and others use the '@' symbol. Does this symbol serve a specific purpose, or is it s ...

Unable to dispatch actions within the mounted lifecycle hook in Vuex?

Can anyone explain why the json data I fetch with axios is not populating my state.pages as expected? Interestingly, when I make a change to the file and vite reloads, the data appears on the page. However, it disappears again upon refreshing the browser. ...

Revolutionizing Form Select Field: Introducing Rails' Dynamic Input Rendering

I'm a beginner in coding, so please bear with me if my question sounds amateurish. Currently, I am developing an e-commerce website where customers can order posters from images uploaded to the site. They should be able to choose the size of the poste ...

Methods for bypassing a constructor in programming

I am working on a code where I need to define a class called programmer that inherits from the employee class. The employee class constructor should have 4 parameters, and the programmer class constructor needs to have 5 parameters - 4 from the employee c ...

What is the correct way to securely send the username and password from a ReactJS frontend to the backend for authentication?

My React application includes an onChange function on a form that collects username and password. Upon submission, the username and password are sent to the server side using redux dispatch in Node.js. On the server side, I am authenticating the credentia ...

Is there a Google Maps feature that displays clusters in a dropdown

Currently, I am utilizing Google Maps to place pins all over the world and implementing markercluster.js to cluster those pins when they are nearby. One feature I am trying to incorporate is the ability to hover over a cluster of pins and have a dropdown d ...

Is there a way to have a span update its color upon clicking a link?

I have 4 Spans and I'm looking to create an interactive feature where clicking a link in a span changes the color of that span. Additionally, when another link in a different span is clicked, the color of that span changes while reverting the previous ...

Error in Browserify Express App: Unexpected token while parsing the file

I have been attempting to browserify a javascript file. When I run the command: browserify global.js -o bundle.js An error message is returned: Error: Parsing file C:\ocquiz\public\javascripts\global.js: Unexpected token (756 ...

The Art of Validating Configurations Using io-ts

I have recently switched to using io-ts instead of runtypes in a fresh project. In terms of configuration validation, my approach involves creating an object that specifies the types for each part of the config; const configTypeMap = { jwtSecret: t.str ...

How can I use JQuery to enable or disable checkboxes upon loading?

I am looking to implement a feature where the checkboxes are enabled when the .group is checked and disabled when it is unchecked. Although I can toggle between them, I'm facing difficulty in disabling the unchecked checkbox using the .group. Upon lo ...