【问题标题】:How to use fetch in TypeScript如何在 TypeScript 中使用 fetch
【发布时间】:2017-04-27 11:12:24
【问题描述】:

我在 Typescript 中使用 window.fetch,但我无法将响应直接转换为我的自定义类型:

我通过将 Promise 结果转换为中间“任何”变量来解决这个问题。

这样做的正确方法是什么?

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;
      })

【问题讨论】:

  • 呃,json 包含普通对象,那么如何将其转换为实例呢?您需要使用 Actor.from 之类的东西来创建带有数据的 new Actor
  • 为什么“不允许”?尝试时遇到什么错误?
  • 你使用了哪些定义,因为fetch isn't in typescript libs yet
  • 啊,对不起,我刚刚发现了错误:我不得不说 res 的类型是 any。 .then((res:any) => { let b = res})。然后它实际上是允许的。 @MeirionHughes 我正在使用肯定类型的 whatwg-fetch.d.ts 文件来使打字稿识别 fetch。
  • @Timo 这个评论应该是针对 Meirion 的吗?

标签: typescript promise fetch


【解决方案1】:

下面是几个示例,从基本到在请求和/或错误处理之后添加转换:

基本:

// 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 */
  })

数据转换:

通常您可能需要在将数据传递给消费者之前对其进行一些调整,例如,解开顶级数据属性。这是直截了当的:

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 */
  })

错误处理:

我认为您不应该直接在此服务中直接捕获错误,而只是让它冒泡,但如果需要,您可以执行以下操作:

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 */
  })

编辑

自从不久前写下这个答案以来,已经发生了一些变化。如 cmets 中所述,response.json&lt;T&gt; 不再有效。不确定,找不到它被删除的位置。

对于以后的版本,您可以:

// 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
    })
}

【讨论】:

  • 谢谢,这是迄今为止我读过的对泛型的最佳解释。为什么 Promise 可以是一种类型仍然有点模糊,而实际上它是具有该类型的数据......
  • 太棒了!我最近一直在探索 TS 的这一部分,所以它有助于我记下我的笔记。哪个部分令人困惑? - 很高兴扩展它
  • 我希望不是 Promise 具有 &lt;T&gt; 类型,而是正在获取的内容。但显然你可以告诉 Promise 类? (Promise 是一个类吗?一个函数?一个对象?)
  • response.json 方法似乎没有被定义为通用方法——在当前的@types/node-fetch 和当前的 TypeScript lib.dom.d.ts 中都没有——所以这个答案现在不可行。所以我想我们必须这样做return response.json() as Promise&lt;T&gt;;?
  • @ChrisW 你是对的,它已经改变了。我已经更新了答案
【解决方案2】:

如果你看一下@types/node-fetch,你会看到正文定义

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

这意味着您可以使用泛型来实现您想要的。我没有测试这段代码,但它看起来像这样:

import { Actor } from './models/actor';

fetch(`http://swapi.co/api/people/1/`)
      .then(res => res.json<Actor>())
      .then(res => {
          let b:Actor = res;
      });

【讨论】:

  • 添加泛型类型会导致Expected 0 type arguments, but got 1,但也许那是因为我不使用node-fetch。原生 fetch 的类型可能不同?
  • 链接文件没有模板化的json()。如果它以前存在,它就不再存在了。
  • 很抱歉挖掘了一个旧帖子,但是,Body.json&lt;T&gt; 签名已在 2018 年与 this commit 从 distinctlyTyped 中删除。浏览node-fetch > /src/body.js 的历史,我不确定我是否见过以这种方式实现json() 的时间。 Nico,你有这个工作的例子吗,否则,我倾向于认为这没有。
  • 嗯,我是从 2017 年的一些代码中得到的......我现在不知道这个状态
【解决方案3】:

实际上,几乎在打字稿中的任何地方,只要传递的类型兼容,将值传递给具有指定类型的函数就可以正常工作。

话虽这么说,以下工作......

 fetch(`http://swapi.co/api/people/1/`)
      .then(res => res.json())
      .then((res: Actor) => {
          // res is now an Actor
      });

我想将我所有的 http 调用包装在一个可重用的类中——这意味着我需要某种方法让客户端以所需的形式处理响应。为了支持这一点,我接受一个回调 lambda 作为我的包装方法的参数。 lambda 声明接受任何类型,如此处所示...

callBack: (response: any) => void

但在使用中,调用者可以传递一个指定所需返回类型的 lambda。我像这样从上面修改了我的代码...

fetch(`http://swapi.co/api/people/1/`)
  .then(res => res.json())
  .then(res => {
      if (callback) {
        callback(res);    // Client receives the response as desired type.  
      }
  });

这样客户端就可以通过回调调用它...

(response: IApigeeResponse) => {
    // Process response as an IApigeeResponse
}

【讨论】:

    猜你喜欢
    • 2023-03-31
    • 2020-09-12
    • 1970-01-01
    • 1970-01-01
    • 2019-03-14
    • 1970-01-01
    相关资源
    最近更新 更多