如何在 TypeScript 中使用提取

我在 Typecript 中使用 窗户,捡回来,但是我不能将响应直接强制转换为我的自定义类型:

我正在通过向一个中间的“ any”变量强制转换瓶装水的结果来解决这个问题。

做这件事的正确方法是什么?

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;
})
266557 次浏览

如果您看一下 @ type/node-get,您将看到主体定义

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

接下来是一些例子,从基本到在请求和/或错误处理之后添加转换:

基础:

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

剪辑

自从前一段时间写下这个答案以来,情况有了一些变化。正如评论中提到的,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
})
}

实际上,在类型脚本的任何地方,只要传递的类型是兼容的,就可以将值传递给具有指定类型的函数。

也就是说,下面的工作..。

 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
}

这是专门为 POST请求编写的。这就是为什么它有“变量”参数。在“ GET”请求的情况下,相同的代码将工作,可选的变量可以被处理

export type FetcherOptions = {
queryString: string
variables?: FetcherVariables
}


export type FetcherVariables = {[key: string]: string | any | undefined}


export type FetcherResults<T> = {
data: T
}


const fetcher = async <T>({queryString,
variables }: FetcherOptions): Promise<FetcherResults<T>> => {
const res = await fetch(API_URL!, {
method: "POST",
headers: {
"Content-Type": "application/json",
// You can add more headers
},
body: JSON.stringify({
queryString,
variables
})
})
const { data, errors} = await res.json()


if (errors) {
// if errors.message null or undefined returns the custom error
throw new Error(errors.message ?? "Custom Error" )
}


return { data }
}

对于这个特定的用例:

”从远程资源获取数据,我们没有控制权,在注入当前应用程序之前需要验证过滤器

我推荐 Zod npm 包裹 Https://www.npmjs.com/package/zod

以下列方式:

// 1. Define a schema


const Data = z.object({
// subset of real full type
name: z.string(),
// unExpectedAttr: z.number(), --> enabling this will throw ZodError
height: z.string(),
mass: z.string(),
films: z.array(z.string()),
});


// 2. Infer a type from the schema to annotate the final obj


type DataType = z.infer<typeof Data>;


(async () => {
try {
const r = await fetch(`https://swapi.dev/api/people/1/?format=json`);
const obj: DataType = Data.parse(await r.json());
console.log(obj); // filtered with expected field in Data Schema
/**
Will log:
{
name: 'Luke Skywalker',
height: '172',
mass: '77',
films: [
'https://swapi.dev/api/films/1/',
'https://swapi.dev/api/films/2/',
'https://swapi.dev/api/films/3/',
'https://swapi.dev/api/films/6/'
]
}
*/


} catch (error) {
if (error instanceof ZodError) {
// Unexpected type in response not matching Data Schema
} else {
// general unexpected error
}
}
})();