How to use fetch in typescript

后端 未结 3 1183
慢半拍i
慢半拍i 2020-11-29 20:07

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

3条回答
  •  囚心锁ツ
    2020-11-29 20:51

    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(url: string): Promise {
      return fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error(response.statusText)
          }
          return response.json()
        })
    
    }
    
    // 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(url: string): Promise {
      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(url: string): Promise {
      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 is no longer valid. Not sure, couldn't find where it was removed.

    For later releases, you can do:

    // Standard variation
    function api(url: string): Promise {
      return fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error(response.statusText)
          }
          return response.json() as Promise
        })
    }
    
    
    // For the "unwrapping" variation
    
    function api(url: string): Promise {
      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
        })
    }
    

提交回复
热议问题