如何在TypeScript中使用fetch

uqzxnwby  于 2023-06-24  发布在  TypeScript
关注(0)|答案(5)|浏览(194)

我在Typescript中使用了window.fetch,但我不能直接将响应转换为我的自定义类型:
我通过将Promise结果转换为一个中间的“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;
      })
umuewwlo

umuewwlo1#

编辑

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

旧答案

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

基础:

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

g6baxovj2#

实际上,在typescript中的几乎任何地方,只要传递的类型兼容,向具有指定类型的函数传递值就可以按需工作。
话虽如此,以下作品…

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

我想把所有的http调用都 Package 在一个可重用的类中--这意味着我需要一些方法让客户端以所需的形式处理响应。为了支持这一点,我接受回调lambda作为我的 Package 器方法的参数。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
}
hzbexzde

hzbexzde3#

如果您查看@types/node-fetch,您将看到body定义

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

7rfyedvj4#

这是专门为POST请求编写的。这就是为什么它有“variables”参数的原因。在“GET”请求的情况下,相同的代码将起作用,vriables可以是可选的

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 }
}
vfwfrxfs

vfwfrxfs5#

对于此特定用例:
从远程资源获取数据,我们无法控制,并且希望在注入当前应用程序之前验证筛选器
推荐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
    }
  }
})();

相关问题