【问题标题】:Type Inferance using Fetch with Typescript使用 Fetch 和 Typescript 进行类型推断
【发布时间】:2021-02-22 20:37:25
【问题描述】:

我编写了一个 Fetch 包装器来使用 Typescript 获取我的 API。我想,当我从我的 API 取回我的数据时,从“任何”类型到我的例如:“用户”类型。

我给你贴一个我的代码框的链接:https://codesandbox.io/s/react-query-error-rkmv1

当我 console.log 在 onSuccess 回调(甚至直接在 fetch 中)时,即使 Typescript 编译器没有抱怨,并且我可以将获取的数据用作“用户”,如果我记录此用户,它不是真正的用户。这是一个我可以“作为”用户使用的对象,但不能作为“用户”使用。

我可以就这样离开它,因为它最终会起作用,而且我的结果是典型的,但我担心它会带来其他我没有想到的问题。

谢谢!

【问题讨论】:

  • 我曾经遇到过同样的强迫症问题,我发现了一个有用的打字稿泛型,叫做Partial。它基本上采用您的界面并使其成为“部分”版本,因此在您的演员阵容中使用data as Partial<T>...我猜试试
  • 感谢您的回答。我试过但它不起作用,我让我的“发布”函数返回 Promise> 和我的 fetch(..).then(data => data as Partial 但这没有任何改变:|跨度>
  • 它并不是真的要改变任何东西,除了你的 tpyings,它更灵活地适应你所处的场景,说你请求的返回数据可能是你返回的部分版本已定义...typescriptlang.org/docs/handbook/utility-types.html
  • 尝试像这样function post<T>(input: Request | string, body: object, init?): Promise<Partial<T>> { ... } 将返回定义添加到您的函数中
  • 是的,我所做的,但最后,我的对象仍然是来自 API(任何)的对象,而不是我定义的对象(User 或 UserWithtoken)

标签: reactjs typescript fetch-api react-typescript react-query


【解决方案1】:

您实际上是在使用和接口。在运行时打字稿接口或任何自定义类型不存在,只是一个普通对象,打字稿的签名。只有类在实际 JavaScript 代码中是“可见的”,但您应该使用 new 运算符来实例化该类。

正如您所说,“它带来了我没有想到的其他问题”。解决方法是在接口之外再创建一个类,这样你就可以和它交互了。

export interface IUserData {
  // id: number; // ID should not be part of the data
  name: string;
  firstName: string;
  email: string;
  password?: string;
}

export class User {
  public readonly attributes: IUserData;

  constructor(data: Partial<IUserData>, public readonly id?: number) {
    // validate and initialize default values of attributes

    this.attributes = { /* all keys with default values*/ };
  }
}

然后,您可以使用该接口作为响应的签名并使用const model = new User(response.data, response.id) 创建一个新实例,例如。这样,您可以将“功能”添加到类中以在实例之间共享。 model.attributes 具有可供使用的原始属性。您可以创建 getter/setter 以直接访问属性等。

Partial&lt;T&gt; 是 typescript 核心的一种实用程序类型,用于告诉对象具有 T 属性,但可选。例如,如果您只为 email 使用 PATCH,这将非常有用。

【讨论】:

  • 您好,谢谢!我尝试了您的解决方案,它正在工作,但我不确定:codesandbox.io/s/react-query-error-forked-c5k2y?file=/src/api/… 我分叉了我的沙箱。我需要在每个 onSuccess 回调中创建“new User()”,我不知道它在哪里有用,因为我丢失了“自动映射”,不是吗?或者我没明白你的意思。但如果可能的话,我只想要 onSuccess 中的“用户”对象(或登录时的 userWithtoken)
  • 我认为您的解决方案比我的要好,但是.. 我不知道我是否以正确的方式实现它,因此,我仍然必须在每个 onSuccess 中创建新的 User 实例,这是......奇怪的事情应该很简单?或者我不知道,什么是更好的解决方案。而且,由于我的 API 使用 HATEOS,我需要一些灵活的东西。例如:我的用户实体有一个带有 ID 的项目实体的链接。如果我想返回带有 idProjectEntity 的 userEntity 怎么办?
  • @Deewens 当您调用fetch().then() 时,您创建实例并将其映射到您的 post() 函数中。除了返回data as T,您可以将其映射到实例并返回它(我的意思是,将您的一部分代码从useAuth.tsx 移动到fetchWrapper.ts)。
  • 顺便说一句,请记住您将原始数据包装在类构造函数中。因此,请确保初始化它需要的所有属性。
  • 顺便说一句,在您的代码中,您没有在 useAuth.tsx 第 2 行导入时导出用户类 (codesandbox.io/s/react-query-error-forked-c5k2y?file=/src/types/…)
【解决方案2】:

所以我注意到,如果您希望 IDE 反映函数的返回,则需要明确定义函数的返回,让我们采用您在链接中提供的 get 函数...

function get<T>(input: Request | string, init?): Promise<Partial<T>> { // so the return is defined here
  const token = localStorage.getItem(localStorageKey);

  const requestInit = {
    method: "GET",
    ...init
  };

  if (token) {
    requestInit.headers = {
      Authorization: `Bearer ${token}`,
      ...requestInit.headers
    };
  }

  return fetch(apiUrl + input, requestInit)
    .then(handleResponse)
    .then((res) => res.json())
    .then((data) => data as T);
}

试一试...谢谢

【讨论】:

猜你喜欢
  • 2019-04-16
  • 2020-04-21
  • 2021-04-15
  • 2018-07-03
  • 1970-01-01
  • 1970-01-01
  • 2019-09-17
  • 1970-01-01
  • 2021-07-28
相关资源
最近更新 更多