如何在 TypeScript 中使用 fetch
Posted
技术标签:
【中文标题】如何在 TypeScript 中使用 fetch【英文标题】:How to use fetch in TypeScript 【发布时间】: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 的吗?
【参考方案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<T>
不再有效。不确定,找不到它被删除的位置。
对于以后的版本,您可以:
// 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 具有<T>
类型,而是正在获取的内容。但显然你可以告诉 Promise 类? (Promise 是一个类吗?一个函数?一个对象?)
response.json 方法似乎没有被定义为通用方法——在当前的@types/node-fetch
和当前的 TypeScript lib.dom.d.ts
中都没有——所以这个答案现在不可行。所以我想我们必须这样做return response.json() as Promise<T>;
?
@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<T>
签名已在 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
【讨论】:
以上是关于如何在 TypeScript 中使用 fetch的主要内容,如果未能解决你的问题,请参考以下文章
如何在 TypeScript 中使用 RequestPromise?
如何在 Typescript 2.1+ 中使用 Bluebird