Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use fetch in TypeScript

I am using window.fetch in Typescript, but I cannot cast the response directly to my custom type:

I am hacking my way around this by casting the Promise result to an intermediate 'any' variable.

What would be the correct method to do this?

import { Actor } from './models/actor';  fetch(`http://swapi.co/api/people/1/`)       .then(res => res.json())       .then(res => {           // this is not allowed           // let a:Actor = <Actor>res;            // I use an intermediate variable a to get around this...           let a:any = res;            let b:Actor = <Actor>a;       }) 
like image 876
Kokodoko Avatar asked Dec 12 '16 14:12

Kokodoko


People also ask

Can we use fetch in TypeScript?

In TypeScript, we can use the fetch function to consume typed response data.

What is fetch () in JavaScript?

The fetch() method in JavaScript is used to request to the server and load the information on the webpages. The request can be of any APIs that return the data of the format JSON or XML. This method returns a promise. Syntax: fetch('url') //api for the get request .


2 Answers

A few examples follow, going from basic through to adding transformations after the request and/or error handling:

Basic:

// Implementation code where T is the returned data shape function api<T>(url: string): Promise<T> {   return fetch(url)     .then(response => {       if (!response.ok) {         throw new Error(response.statusText)       }       return response.json<T>()     })  }  // Consumer api<{ title: string; message: string }>('v1/posts/1')   .then(({ title, message }) => {     console.log(title, message)   })   .catch(error => {     /* show error message */   }) 

Data transformations:

Often you may need to do some tweaks to the data before its passed to the consumer, for example, unwrapping a top level data attribute. This is straight forward:

function api<T>(url: string): Promise<T> {   return fetch(url)     .then(response => {       if (!response.ok) {         throw new Error(response.statusText)       }       return response.json<{ data: T }>()     })     .then(data => { /* <-- data inferred as { data: T }*/       return data.data     }) }  // Consumer - consumer remains the same api<{ title: string; message: string }>('v1/posts/1')   .then(({ title, message }) => {     console.log(title, message)   })   .catch(error => {     /* show error message */   }) 

Error handling:

I'd argue that you shouldn't be directly error catching directly within this service, instead, just allowing it to bubble, but if you need to, you can do the following:

function api<T>(url: string): Promise<T> {   return fetch(url)     .then(response => {       if (!response.ok) {         throw new Error(response.statusText)       }       return response.json<{ data: T }>()     })     .then(data => {       return data.data     })     .catch((error: Error) => {       externalErrorLogging.error(error) /* <-- made up logging service */       throw error /* <-- rethrow the error so consumer can still catch it */     }) }  // Consumer - consumer remains the same api<{ title: string; message: string }>('v1/posts/1')   .then(({ title, message }) => {     console.log(title, message)   })   .catch(error => {     /* show error message */   }) 

Edit

There has been some changes since writing this answer a while ago. As mentioned in the comments, response.json<T> is no longer valid. Not sure, couldn't find where it was removed.

For later releases, you can do:

// Standard variation function api<T>(url: string): Promise<T> {   return fetch(url)     .then(response => {       if (!response.ok) {         throw new Error(response.statusText)       }       return response.json() as Promise<T>     }) }   // For the "unwrapping" variation  function api<T>(url: string): Promise<T> {   return fetch(url)     .then(response => {       if (!response.ok) {         throw new Error(response.statusText)       }       return response.json() as Promise<{ data: T }>     })     .then(data => {         return data.data     }) } 
like image 82
Chris Avatar answered Sep 28 '22 11:09

Chris


If you take a look at @types/node-fetch you will see the body definition

export class Body {     bodyUsed: boolean;     body: NodeJS.ReadableStream;     json(): Promise<any>;     json<T>(): Promise<T>;     text(): Promise<string>;     buffer(): Promise<Buffer>; } 

That means that you could use generics in order to achieve what you want. I didn't test this code, but it would looks something like this:

import { Actor } from './models/actor';  fetch(`http://swapi.co/api/people/1/`)       .then(res => res.json<Actor>())       .then(res => {           let b:Actor = res;       }); 
like image 26
nicowernli Avatar answered Sep 28 '22 10:09

nicowernli