Typescript – How to use fetch in TypeScript

fetch, promise, 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;      })

Best Solution

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

Typescript – How to use fetch in TypeScript

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

Typescript – How to use fetch in TypeScript

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 sameapi<{ title: string; message: string }>('v1/posts/1')  .then(({ title, message }) => {    console.log(title, message)  })  .catch(error => {    /* show error message */  })

Typescript – How to use fetch in TypeScript

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 sameapi<{ title: string; message: string }>('v1/posts/1')  .then(({ title, message }) => {    console.log(title, message)  })  .catch(error => {    /* show error message */  })

Typescript – How to use fetch in TypeScript

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 variationfunction 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" variationfunction 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    })}