Applying a Typescript Generic to enhance the functionality of the API fetcher

I've written a simple function to enhance fetch functionality. I am currently exploring how TypeScript Generics can be utilized to define the Type for 'data' in the return.

const apiFetchData = async (
    url: string,
    options = {},
): Promise<FetchResponse> => {
    try {
        const res = await fetch(url, options);
        const data = (await res.json()) || null;
        const statusCode = res.status;

        return {
            data,
            hasData: !!data,
            statusCode,
            error: (statusCode !== 200 && true) || false,
            errorType: (statusCode !== 200 && 'api') || null,
        };
    } catch (error) {
        return {
            data: {
                error: error.message,
            },
            hasData: false,
            statusCode: 500,
            error: true,
            errorType: 'application',
        };
    }
};

The structure of the FetchResponse type is as follows:

type FetchResponse = {
  data: {
    [key: string]: {};
    error?: string;
  };
  hasData: boolean;
  statusCode: number;
  error: boolean | null;
  errorType?: 'api' | 'application' | null;
};

If my API response will contain the following format:

type User = {
  name: string;
  email: string;
}

Then I aim for my FetchResponse to look like this:

type FetchResponse = {
  data: User;
  hasData: boolean;
  statusCode: number;
  error: boolean | null;
  errorType?: 'api' | 'application' | null;
};

This would allow me to do something similar to the following:

const response = await apiFetchData('URL');
console.log('User name is', response.data.name);

Currently, I use the following approach, which functions but seems lengthy:

type FetchWithUserType = FetchResponse & {
    data: {
        error?: string;
        user?: User;
    };
};

and then:

const response: FetchWithUserType = await apiFetchData('URL');
console.log('User name is', response.data.name);

If generics are not the appropriate solution, I welcome assistance in improving this process so I can achieve my objective. The main challenge lies in having VSCode intelligently recognize the content within the returned data instead of being generic and constantly switching between code views.

Answer №1

It seems a bit unclear what you're trying to convey with the statement "I know my API will return the following". It's assumed that you don't mean your API will consistently return that specific data or else you would have simply included it in the FetchResponse definition.

If you are indicating that you expect the API to return data structured in a certain way, you can utilize generics like this:

async function apiFetchData<T extends {[key:string]: any}>(
// function parameters not shown
):Promise<FetchResponse<T>> => {
// function implementation not shown
}

Then you define FetchResponse as:

type FetchResponse<S> = {
   data: <S>
   // rest of the type
}

This approach essentially informs TypeScript that you have foreknowledge of the data structure, allowing it to be inserted into the FetchResponse type accordingly.

If instead you are uncertain about the API response beforehand but believe it may conform to a certain format like User, then utilizing a type guard would be necessary. However, it appears that the former example is more relevant in this context.

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

Issue: The last loader (./node_modules/awesome-typescript-loader/dist/entry.js) failed to provide a Buffer or String

This issue arises during the dockerhub build process in the dockerfile. Error: The final loader (./node_modules/awesome-typescript-loader/dist/entry.js) did not return a Buffer or String. I have explored various solutions online, but none of them have pr ...

I am seeking guidance for developing my own messaging platform, similar to Discord, using Typescript

Allow me to simplify this for you. This piece of code outlines TypeScript interfaces and namespaces for a WebSocket API that is commonly used in a chat or messaging system. It seems to define the format of messages being exchanged between a client and ser ...

Updating an array of drag and drop elements in Angular Material

During my attempt to use drag and drop functionality with Angular Material, I encountered an issue with updating the `pos` key in a JSON array. Specifically, I wanted to set the `pos` value to the value of `event.currentIndex` while also adjusting the posi ...

Using Vue.js 2 on multiple HTML pages with Typescript and ASP.Net Core

My ASP.Net Core MVC project utilizes VueJs2 for more complex tasks, with each view having its own corresponding js file. The directory structure is as follows: ├ Controllers\HomeController.cs (with actions Index & Details) ├ Scripts\Hom ...

Difficulties with managing button events in a Vue project

Just getting started with Vue and I'm trying to set up a simple callback function for button clicks. The callback is working, but the name of the button that was clicked keeps showing as "undefined." Here's my HTML code: <button class="w ...

"Exploring the world of TypeScript Classes in combination with Webpack

If I create a TypeScript class with 10 methods and export a new instance of the class as default in one file, then import this class into another file (e.g. a React functional component) and use only one method from the class, how will it affect optimizati ...

Navigating through different components within a single page

Each segment of my webpage is a distinct component, arranged consecutively while scrolling e.g.: <sectionA></sectionA> <sectionB></sectionB> <sectionC></sectionC> All the examples I've come across involve creating ...

Is there a way to deactivate multiple time periods in the MUI Time Picker?

Recently, I have been working on implementing a Time Picker feature with two boxes: [startTime] and [endTime]. The objective is to allow the time picker to select only available times based on predefined data: let times = [ { startTime: "01:00", en ...

Why is the 'as' keyword important in TypeScript?

class Superhero { name: string = '' } const superheroesList: Superhero[] = []; const superheroesList2 = [] as Superhero[]; As I was exploring TypeScript, I stumbled upon these two distinct methods of declaring an array. This got me thinking w ...

While using axios to make a GET request, I encountered errors when checking for .isSuccess in react

const searchInvoiceList = async ( plantLocation: string, invoiceType: string ) => { let dataList: InvoiceData[] = []; await axios .get(`${linkURL}inv/getControlList/${plantLocation}/${invoiceType}`) .then((response) => { dataLis ...

Ensuring typescript req.user in Passport JS is always defined: Best practices

When utilizing Passport JS, the req.user within the route is considered potentially undefined. However, the middleware prior to my route method ensures that this scenario does not occur. How can I convey this information to TypeScript? Object may be &apos ...

Flex-Layout in Angular - The Ultimate Combination

Recently, I decided to delve into Angular and the Flex-Layout framework. After installing flex-layout through npm, I proceeded to import it in my app.module.ts file like so: import { FlexLayoutModule } from '@angular/flex-layout'; imports: [ Fl ...

Performing Jasmine unit testing on a component that relies on data from a service, which itself retrieves data from another service within an Angular 2+ application

Attempting to implement unit testing for a service using httpmock has been challenging. The service in question utilizes a method to make http get calls, but I have encountered difficulties in writing the test cases. saveservice.service.ts -- file const ...

Determining block time based on block number within Polygon Mumbai Testnet

Is there a dependable method to identify the production time of a specific block in Polygon Mumbai Testnet using only its block number? I am unable to utilize an Api for this purpose and am seeking a more user-friendly computational solution. Any suggest ...

Exporting Arrays in Ionic Angular with Typescript is an essential feature

When trying to access an exported component in Typescript, there may be issues with importing the module correctly into another component. An error message is displayed when calling the AddToArray method: Cannot read property 'push' of undefi ...

PhpStorm does not currently support types in JavaScript

Currently, I am using PhpStorm for a Vue 2 / TypeScript project. However, whenever I attempt to add return types to functions, I encounter the error message "Types are not supported by current JavaScript version": https://i.sstatic.net/ct3gu.png In the " ...

Angular/TypeScript restricts object literals to declaring properties that are known and defined

I received an error message: Type '{ quantity: number; }' is not assignable to type 'Partial<EditOrderConfirmModalComponent>'. Object literal may only specify known properties, and 'quantity' does not exist in type &ap ...

Tips for accessing the FormControlName of the field that has been modified in Angular reactive forms

My reactive form consists of more than 10 form controls and I currently have a subscription set up on the valueChanges observable to detect any changes. While this solution works well, the output always includes the entire form value object, which includ ...

Strategies for patiently waiting for an object to appear before loading the HTML

After logging into my service, I download data from a REST API, and based on that data, I display certain tabs. However, I am experiencing issues with loading the HTML content once the data has been retrieved. The ngif directive at the beginning of the H ...

I am having trouble getting the guide for setting up a NextJS app with Typescript to function properly

For some time now, I have been experimenting with integrating Typescript into my NextJS projects. Initially, I believed that getting started with Typescript would be the most challenging part, but it turns out that installing it is proving to be even more ...