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

Step-by-step guide to minify Typescript files with Webpack

I am facing an issue in my webpack configuration while trying to minify the output bundle when working with TypeScript. Currently, only one file has been migrated to TypeScript in my project and it works fine with babel-node and the dev bundle without Ugli ...

What is the best way to obtain the value of a nested formBuilder group?

Currently, my nested form is structured like this: this.form = this.formBuilder.group({ user: this.formBuilder.group({ id: ['', Validators.required], name: ['', Validators.required], phone: ['' ...

TypeScript's type 'T' has the potential to be instantiated with any type, even if it is not directly related to 'number'

Let's say we have a function that takes a value (for example, number) and a callback function that maps that value to another value. The function simply applies the provided callback: function mapNumber<T>(value: number, mapfn: (value: number) = ...

Ways to expose a declared module in Enzyme's shallow rendering?

I've been grappling with how to ensure that my custom declared module is detected when I shallow render a component using enzyme in Jest unit tests. The issue revolves around a specific module declaration named _aphrodite, which looks like this: // i ...

What could be causing my webpage to automatically refresh following a POST request in NodeJS?

Utilizing the express framework alongside NodeJS, I have encountered an issue where my client webpage refreshes after making a POST request that triggers a python script and returns a JSON object to the client. My dilemma lies in preventing this automatic ...

Is it advisable to utilize TypeScript interfaces for declaration files and React component prop definitions?

Whenever I create structures for object types, my go-to method is to define them in my declaration.d.ts file like this: type TagsObject = { _id: string; tag: string; } type ProjectData = { _createdAt: string; _id: string; _rev: string; _type: ...

What is the best way to retrieve a specific field from the observable data stream?

When working with observables, I often find myself using them like this: ... const id = 1337; this.service.getThing(id).subscribe( suc => doSomething(suc.name), err = doSomethingElse() ); Lately, I've been utilizing the async pipe more freque ...

Using TypeScript with React: Automatically infer the prop type from a generic parameter

I've developed a component that retrieves data from an API and presents it in a table. There's a prop that enables the fetched value to be displayed differently within the table. Here's how I currently have the prop types set up: // T repres ...

A warning has been issued: CommonsChunkPlugin will now only accept one argument

I am currently working on building my Angular application using webpack. To help me with this process, I found a useful link here. In order to configure webpack, I created a webpack.config.js file at the package.json level and added the line "bundle": "web ...

Encountering an issue when attempting to establish a connection to Redis using a cache manager within a Nest

Incorporating the NestJS framework into my project and utilizing Cash Manager to connect with Redis cache. Successfully connected with Redis, however encountering an error when attempting to use methods like set/get which shows 'set is not a function& ...

How to access type properties in typescript without using the "this" keyword

Below is a snippet of code that I am working with: class Player implements OthelloPlayer { depth; constructor(depth: number) { this.depth = depth; } getMove(state: OthelloState) { return this.MinimaxDecision(stat ...

How can input be prevented on keydown within angular6?

Is there a way to disable the input field only when a keydown event occurs, without affecting other input fields? xyz.component.html <input type="text" (keydown)="disableInput($event)"> // <-- Disable if keydown <input type="text" (keydown) ...

Forming a distinct array from a collection of objects

Describing demoList: demoList = [ { id: 1, validFrom: "2019-06-01T00:00:00", validTo: "2020-06-17T00:00:00", xxxM: 50, xxxN: 2.2, xxxQ45: 2, xxxQ100: 1.65, xxxQ125: null, xxxQ150: null, xxxQ2 ...

Error message displaying that the argument for the onChange state in a jhipster react form is not assignable as type '{ [x: number]: any; }'

Just diving into the world of React and encountering a bit of a struggle with jsx when it comes to setting state in a form that contains two fields and triggers an ajax call to store a json object (response data) in the state's field3. The code I curr ...

"Organize your files with React and TypeScript using a file list

interface IVideos { lastModified: number, name: string, path: string, size: number, type: string, webkitRelativePath: string } const [videos, setVideos] = useState<IVideos[] | null>([]); <input type="file" onChange={(event) => ...

Tips for incorporating confidence intervals into a line graph using (React) ApexCharts

How can I utilize React-ApexCharts to produce a mean line with a shaded region to visually represent the uncertainty of an estimate, such as quantiles or confidence intervals? I am looking to achieve a result similar to: ...

Click function for mat-step event handler

Is it feasible to create a click event for the mat-step button? I want to be able to add a (click) event for each mat-step button that triggers a method. Essentially, I am looking to make the mat-step button function like a regular button. You can find mo ...

The error message indicates that the 'aboutData' property is not found within the 'never[]' data type

What is the correct method for printing array elements without encountering the error message "Property 'post_title' does not exist on type 'never[]'?" How can interfaces be used to define variables and utilize them in code for both ab ...

Is there a way to establish a pre-defined key in a mat-table?

I am fetching data from an API and I need to display it in a key-value format on a mat table. The keys are predefined and not provided by the API. The data retrieved from the API is shown here: image1 I want to display it on a mat table like this: mat ta ...

The proper method of retrieving data into an array using bind_params in PHP

Struggling to execute a MySQL query and save the results into an array by using bind_params for two parameters: userUniqueId (String) and limit (used to LIMIT query records). Despite numerous attempts, the code isn't functioning correctly. The query w ...