Typescript fetch implementation

I've been researching how to create a TypeScript wrapper for type-safe fetch calls, and I came across a helpful forum thread from 2016. However, despite attempting the suggestions provided in that thread, I am still encountering issues with my code.

The TS Linter is flagging an error when I try to use this code snippet: response.json<T>(), stating "Expected 0 type arguments, but got 1."

After reading through another forum post, I made adjustments as follows:

data => data as T

Unfortunately, both options above are not working as intended, and they return undefined. When using a source like this one, I expect to be able to specify the structure like so:

api<{ name: string; username: string }>('https://jsonplaceholder.typicode.com/users')

The subsequent clause then should be:

.then(({ name, username })

I would greatly appreciate a detailed guide on creating a wrapper function for performing type-safe fetch calls.

EDIT

As requested, here is a more comprehensive code snippet:

api<T>(url: string): Promise<T> {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(response.statusText);
      }
      return response.json().then(data => data as T);
    })
    .catch((error: Error) => {
      throw error;
    });
}

// consumer
this.api<{ name: string; username: string }>("https://jsonplaceholder.typicode.com/users/")
  .then(({ name, username }) => {
    console.log(name, username);
  })
  .catch(error => {
    console.error(error);
  });

Answer №1

The Typescript portion is solid. Implementing as T within the json method should perform correctly.

The issue arises from your json including an array, not a single object. To make the remaining code function properly, adjust it to read

return response.json().then(data =>  data[0]);
selecting only one element.

If you require the entire array, modify the consumer to transmit an array of the expected type:

this.api<Array<{ name: string; username: string }>>("https://jsonplaceholder.typicode.com/users/")
    .then(data  => {
       data.forEach(({ name, username }) => console.log(name, username))
    })
    .catch(error => {
      console.error(error);
    });

Answer №2

Separately compiling the backend and frontend means you cannot perform type checking across both. However, you can use shared types/interfaces between the two like so:

const res = await fetchJSON<ApiReqLogin, ApiResLogin>('api/login', { code })

ApiReqLogin represents the type/interface for the request, while ApiResLogin pertains to the expected response.

Ensure on the server side that the response matches the ApiResLogin type, and similarly validate on the frontend that the request conforms to ApiReqLogin.

Here is a simple fetch wrapper function:

const fetchJSON = <Req, Res>(link: string, body: Req): Promise<{ data: Res; mes: false } | { data: false; mes: string }> => {
  return new Promise(resolve => {
    fetch(link, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    })
      .then(res => res.json())
      .then((data: Res) => {
        resolve({ data, mes: false })
      })
      .catch(err => {
        resolve({ data: false, mes: 'error' })
      })
  })
}

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

Implementing the "$store" property within Vue components

Click here for a guide on how to type the $store property. Unfortunately, I've been encountering issues with it. In my Vue 2 project created using vue-cliI, I included a vuex.d.ts file in ./src directory but the $store property in my components still ...

Using Long Polling with Angular 4

I am looking for a way to monitor the progress of a certain task using API calls. To achieve this, I have developed a service that executes these API calls every 1.5 seconds Main Component private getProgress() { this.progressService.getExportPr ...

Attempting to eliminate any dates that have already occurred

I am faced with an array containing various dates in string format such as "2016-08-12". My goal is to eliminate any dates that have already passed by comparing them to today's date. I am using TypeScript for this task. Here is a snippet of my datoAr ...

Steps for utilizing a function from the parent component to initialize TinyMCE

I have implemented an uploading function in my parent component. As I set up tinymce, I connected the [init] property of my component to the loadConfig() function. <editor [(ngModel)]="data" [init]="loadConfig()"></editor> The loadConfig func ...

Ways to attach an event listener to a useRef hook within a useEffect hook

As I work on creating a custom hook, I am faced with the task of adding an event listener to a ref. However, I am uncertain about how to properly handle cleaning up the event listener since both listRef and listRef.current may potentially be null: const ...

Error in React Typescript: No suitable index signature with parameter type 'string' was located on the specified type

I have encountered an issue while trying to dynamically add and remove form fields, particularly in assigning a value for an object property. The error message I received is as follows: Element implicitly has an 'any' type because expression o ...

Extending Error object disrupts `instanceof` validation in TypeScript

Could someone clarify why the error instanceof CustomError part of the code below returns false? class CustomError extends Error {} const error = new CustomError(); console.log(error instanceof Error); // true console.log(error instanceof CustomError); ...

Expanding external type declarations within Typescript

I am currently working with Typescript and the ant design library. My goal is to extend an existing interface by adding a single property. To start, I imported the original interface so you can see the folder structure: import { CollapseProps } from &apo ...

Transforming the date from JavaScript to the Swift JSON timeIntervalSinceReferenceDate structure

If I have a JavaScript date, what is the best way to convert it to match the format used in Swift JSON encoding? For example, how can I obtain a value of 620102769.132999 for a date like 2020-08-26 02:46:09? ...

Creating a universal timer at the application level in Angular

Is there a way to implement a timer that will automatically execute the logout() function in my authentication.service at a specific time, regardless of which page I am currently on within my application? I attempted to create a timer within my Authentica ...

Expanding Angular FormGroup Models with TypeScript

I've developed a foundational model that serves as a base for several other form groups. export class BaseResource { isActive: FormControl; number: FormControl; name: FormControl; type: FormControl; constructor( { ...

The never-ending scroll feature in Vue.js

For the component of cards in my project, I am trying to implement infinite scrolling with 3 cards per row. Upon reaching the end of the page, I intend to make an API call for the next page and display the subsequent set of cards. Below is my implementatio ...

Can we set a specific length for an array passed in as a prop?

Can we use Typescript to specify the exact length of an array coming from props? Consider the following array of objects: const sampleArray = [ { key: '1', label: 'Label 1', value: 9 }, { key: '2', label: 'Label 2&ap ...

Tips for declaring a particular type during the process of destructuring in Typescript

I have my own custom types defined as shown below: export type Extensions = | '.gif' | '.png' | '.jpeg' | '.jpg' | '.svg' | '.txt' | '.jpg' | '.csv' | '. ...

Is it possible to pass a Styled Components Theme as Props to a Material UI element?

After spending 9 hours scouring the internet for a solution, I am at my wit's end as nothing seems to work. Currently, I am developing a React component using TypeScript. The issue lies with a simple use of the Material UI Accordion: const Accordion ...

Issue with cordova plugin network interface connectivity

I'm currently working with Ionic 2 Recently downloaded the plugin from https://github.com/salbahra/cordova-plugin-networkinterface Attempting to retrieve IP addresses without utilizing global variables or calling other functions within the function ...

Using a static class reference as a parameter in a generic type leads to a error

Before I submit a ticket on github, I want to double-check that I'm not making any mistakes. The issue should be clear enough: class A {} class B { static A = A; } function foo<T>(arg: T) {} // this is valid const b = new B.A; // "B" only ...

Tips for reverting from Angular 7 to Angular 6

I attempted to switch from angular 7 back to angular 6 by executing the following npm commands: npm uninstall -g angular-cli npm cache clean npm install -g <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="32535c55475e53401f515e5 ...

Transferring information from child to parent class in TypeScript

I have a scenario where I have two classes (Model). Can I access properties defined in the child class from the parent class? Parent Class: class Model{ constructor() { //I need the table name here. which is defined in child. } publ ...

Minimize overlap across both projects

One scenario is having two projects that utilize a lot of the same components. How can we minimize redundancy between them? Is there a way to make them shareable? Perhaps leveraging webpack could be the solution? ...