Ways to develop a dynamic Promise-returning Function

I find myself in a situation where I am aware of the type of data that will be returned when making a request to an API.

Here is the function I have:

const fetchData = async (url: string, options?: Object): Promise<any> => {
  const res = await fetch(url, options);
  const data = await res.json();
  return data;
};

Although this function returns a Promise of type any, I am looking for a way to make the response more typed regardless of the input type. How can I achieve this?

One idea I had was to define interfaces like this:

interface One{
...
}
interface Two{
...
}

const fetchData = async (url: string, options?: Object): Promise<One | Two> => {
  const res = await fetch(url, options);
  const data = await res.json();
  return data;
};

However, this approach may not be ideal as it requires creating a new interface every time a request is made to a different data source.

So, how can I make the fetchData function more dynamic in returning a typed Promise?

Thank you for your assistance

Answer №1

In my opinion, a more efficient design strategy involves creating methods with specific names rather than relying on a generic method. Each function name should clearly indicate its purpose, enhancing specificity. Below is the design I would recommend:

class ApiService {

  // method to fetch states from the API call
  private static fetchState(url: string, options: StateOptions): Promise<State[]> {
      return ApiService.fetchData(url, options);
  }
  
  // private method, only accessible in ApiService class
  private static async fetchData(url: string, options?: any): Promise<any> {
      const res = await fetch(url, options);
      const data = await res.json();
      return data;
  }

}

It is advisable not to consolidate all API calls within a single class; you can categorize them by extending the APIService class and by defining the fetchData method as protected.

The advantages of this approach include:

  1. Defining interfaces for options and return types
  2. Function names directly correspond to the API call, aiding in debugging and code readability
  3. Catering to special cases like data preprocessing or constant definitions within specific functions. For instance, if a language parameter like en is required for the state API call, it's preferable to hardcode it in the ApiService.fetchState method rather than passing it as an argument with every function call.

I have opted for an object-oriented approach, but you can also implement the same functionality using a functional approach. The key principle is to avoid relying on a generic method throughout your codebase, as it can lead to scalability issues and complex maintenance.

This recommendation is based on my personal experience.

Addressing your query:

How to create a Function that returns a Promise type dynamically?

You can achieve this using generics in TypeScript.

async function fetchData<T>(url: string): Promise<T> {
    const result: any = {};
    return result;
}

interface State {
    code: string;
    name: string;
}

interface Country {
    code: string;
    name: string;
}

const fetchStates = fetchData<State[]>("/fetch/state");
const fetchCountry = fetchData<Country[]>("/fetch/country");

Link to TS Playground

Answer №2

If you're looking to diversify, simply tweak or introduce a new type T into the mix. Explore Ts Playground

 type T = string | number;
    const fetchingData = async (url: string, options?: Object): Promise<T> => {
      const res = await fetch(url, options);
      const data = await res.json();
      return data;
    };

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 relationship between Typescript references, builds, and Docker?

I am facing a dilemma with my projectA which utilizes a common package that is also needed by my other Nodejs services. I am unsure of the best approach to package this in a Docker file. Ideally, running tsc build would compile both the project and the dep ...

Clearing error messages from a form using the reset button or after cancelling the form

I am having trouble removing the error outline around the input box and error messages displayed below it. When I cancel the form or click on the reset button, the input fields' content along with the error messages should be cleared. However, current ...

NestJS Ensures Type Safety for Mongoose Models, but Model Functions Expecting Incorrect Types (Any)

Shema Interfaces export interface MyCat { name: string; color: string; } export type Cat = MyCat & Document; export const CatSchema = new Schema({ name: { type: String, required: true, }, color: { type: String, required: tr ...

Extract all objects from an array where a specific field contains an array

data:[ { id:1, tags:['TagA','TagB','TagC'] }, { id:2, tags:['TagB','TagD'] }, { id:3, tags:[&a ...

Using @carbon/react in conjunction with Next.js version 13 leads to unconventional styling

Here's what I did to set up my Next.js application: npx create-next-app@latest I then installed the necessary package using: npm i -S @carbon/react The global styles in app/globals.scss were customized with this code snippet: @use '@carbon/reac ...

Discrepancy between code, output from ng build, and how the browser is

I am in the process of developing an Angular 13 library that includes two services and a group of models. These services consist of an authentication service and a client service. However, after building the library, I noticed some global variables missing ...

having trouble retrieving 200 status code from Angular server response

Objective: I need to make certain changes to the record locally if the server responds with a 200 code. Problem: I am unable to retrieve the response code or access the 'message' attribute. This is the server response I receive from the HTTP ca ...

Prisma and Next.js: Changes to content require re-deployment for updates to take effect

Just recently, I launched a new website on Vercel. My web application is being built with Prisma and Next.js. However, I'm currently facing an issue where the content doesn't update in real-time unless I manually re-deploy the application. Here&a ...

Expanding a Zod object by merging it with a different object and selecting specific entries

Utilizing Zod, a TypeScript schema validation library, to validate objects within my application has led me to encounter a specific scenario. I find myself in need of validating an object with nested properties and extending it with another object while se ...

Typescript on the client-side: what is the best way to eliminate circular dependencies when using the factory method design pattern?

In my code, I have implemented the factory method pattern. However, some instances using this pattern end up with circular dependencies. Removing these dependencies has proven to be a challenge for me. To illustrate, consider the following example: // fact ...

I'm trying to figure out how to incorporate types for utilizing Intl.ListFormat in node v12. Can

I am currently working with nodeJS version 12.10.0, which now includes support for Intl.ListFormat. I am also using Typescript version 3.6.3. However, when compiling my code with Typescript, I encounter the following error: Property 'ListFormat' ...

Exploring the features of NextJS version 13 with the benefits

Starting from the 13th step, SSR is utilized by default and in order to opt for client side rendering you must specify it at the top like so: 'use client' Currently, my setup involves TypeScript and styled-component integration. Take a look at ...

The proper method for specifying contextType in NexusJS when integrating with NextJS

I am currently facing a challenge while trying to integrate Prisma and Nexus into NextJS. The issue arises when I attempt to define the contextType in the GraphQL schema. Here is how I have defined the schema: export const schema = makeSchema({ types: [ ...

What is the best approach to eliminate the 'false' type within a setState function in React?

Hey, I've been working on a project that involves using the useState hook and dealing with state using generics. I encountered an issue where I manipulated a fetched array within a setState function using the filter method, which resulted in returnin ...

What is the best way to define a global variable in TypeScript and access it throughout a Vue application?

In my main.ts file, I am looking to define a variable that can be accessed in all Vue files. Within my sfc.d.ts file, the following content is included: declare module '*.vue' { import Vue from 'vue' export default Vue } declar ...

What is the best way to use lodash to group objects that contain nested objects?

Currently utilizing Typescript in conjunction with Lodash! Upon retrieving data from the database, here is the resulting output: [ { "unitPrice": 0.01, "code": "92365524", "description": "Broto gr ...

The interface 'IProduct' does not include several properties found in type 'IProduct[]', such as length, pop, push, concat, and many more

My goal is to transfer data between parent and child components using React and TypeScript. I have defined the following interfaces: export interface IProduct { id: string; name: string; price: string; image: string; ...

InitAuth0 Auth0 encountering deepPartial error in Next.js with TypeScript setup

Having some trouble setting up auth0 with nextjs using typescript. When I try to initialize Auth0, I encounter an error regarding deep partials, Argument of type '{ clientId: string; clientSecret: string; scope: string; domain: string; redirectUri: st ...

The attribute 'split' is not found on the never data type

I have a function that can update a variable called `result`. If `result` is not a string, the function will stop. However, if it is a string, I then apply the `split()` method to the `result` string. This function always runs successfully without crashin ...

The error you are seeing is a result of your application code and not generated by Cypress

I attempted to test the following simple code snippet: type Website = string; it('loads examples', () => { const website: Website = 'https://www.ebay.com/'; cy.visit(website); cy.get('input[type="text"]').type(& ...