【问题标题】:Typescript Fetch API return Promise<T> or Promise<T[]>Typescript Fetch API 返回 Promise<T> 或 Promise<T[]>
【发布时间】:2019-12-19 21:41:52
【问题描述】:

我正在使用 Typescript 编写 API 包装器,并且正在考虑使用 Fetch API 和 Promises。我想在请求方法中使用泛型,以便我可以对提供的类型进行建模,但是我对 Typescript(和承诺,就此而言)相当陌生,并且在有条件地返回 Promise&lt;T&gt; 或 Promise&lt;T[]&gt; 时找不到任何东西,因为并非所有请求都会返回一个数组。

这是我的代码的状态,到目前为止,它还没有处于完成状态,但它是可行的。

export default class ApiWrapper
{
    public async request<T>(endpoint: string, method: string = 'GET', parameters: object = {}) : Promise<void|T>
    {
        var protocol = process.env.REACT_APP_APPLICATION_PROTOCOL || 'https'
        var host = process.env.REACT_APP_APPLICATION_HOST
        var port = process.env.REACT_APP_APPLICATION_PORT ? `:${process.env.REACT_APP_APPLICATION_PORT}` : ''
        var path = process.env.REACT_APP_APPLICATION_PATH || '/'

        if (!host)
        {
            // TODO:- Handle error
        }

        let body = JSON.stringify(parameters)

        return await fetch(`${protocol}://${host}${port}${path}/${endpoint}`, {
            method: method,
            body: body
        })
        .then(response => response.json() as Promise<T>)
        .then(data => {
            return data
        })
    }

    public async get<T>(endpoint: string, parameters: object = {})
    {
        return await this.request<T>(endpoint, 'GET', parameters)
    }

    public async post(endpoint: string, parameters: object)
    {
        return await this.request(endpoint, 'POST', parameters)
    }

    public async put(endpoint: string, parameters: object)
    {
        return await this.request(endpoint, 'PUT', parameters)
    }

    public async delete(endpoint: string)
    {
        return await this.request(endpoint, 'DELETE')
    }
}

我可以尝试像这样有条件地返回:

return await fetch(`${protocol}://${host}${port}${path}/${endpoint}`, {
    method: method,
    body: body
})
.then(response => {
    if (Array.isArray(response.json())) {
        return response.json() as Promise<T[]>
    } else {
        return response.json() as Promise<T>
    }
})
.then(data => {
    return data
})

虽然T 不能分配给类型T[]。这可能非常简单,愚蠢甚至不可能,但任何指针都会受到赞赏。如果有,可以提供任何其他信息

【问题讨论】:

  • @MoshFeu 哦,有趣;我什至不知道 TS 有过载!调用request() 时如何区分方法签名?鉴于我提前知道它是从端点返回的数组还是实例。
  • 如果我理解这个问题,你在声明变量时声明类型。 const arr: Array = request(...
  • 我的意思是,如果我要重载 request 方法,参数将是相同的,并且声明 public async request&lt;T[]&gt;(endpoint: string, method: string = 'GET', parameters: object = {}) 将无效,因此编译器会抱怨我使用相同的方法签名(函数声明两次)。所以我正在寻找一种方法,既可以说let users: User[] = await this.request&lt;User&gt;('users', 'GET'),又可以说let user: User = await this.request&lt;User&gt;('users/12', 'GET'),并且能够处理数组和单个实例

标签: typescript fetch-api


【解决方案1】:

好的,我想我想通了,尽管我确信这可能会有所缩短。

如果我通过 not 使用箭头函数在 .then() 回调中声明 Promise 的返回类型,如下所示:

return await fetch(`${protocol}://${host}${port}${path}/${endpoint}`, {
    method: method,
    body: body
})
.then(function(response): Promise<T|T[]> {
    if (response.ok) {
        if (Array.isArray(response.json())) {
            return response.json() as Promise<T[]>
        } else {
            return response.json() as Promise<T>
        }
    } else {
        throw new Error(response.statusText)
    }
})
.then(data => {
    return data
})

然后编译器似乎接受了这一点。这可能应该很明显,但 TypeScript 编译器错误是一个迷宫。

【讨论】:

    【解决方案2】:

    您不需要使用不同的签名返回相同的结果。 T不仅可以是单一类型(如string、number等),还可以是一个类型的数组。

    另外,我看不到函数在哪里void。如果您打算抛出错误,则正确的签名是never。

    所以,函数可以如下所示:

    class ApiWrapper {
      public async request<T>(endpoint: string, method: string = "GET", parameters: object = {}): Promise<never | T> {
        let protocol, host, port, path;
        let body = JSON.stringify(parameters);
    
        return await fetch(`${protocol}://${host}${port}${path}/${endpoint}`, {
          method: method,
          body: body
        })
          .then(response => {
            if (response.ok) {
              return response.json();
            } else {
              throw new Error(response.statusText);
            }
          })
          .then(data => {
            return data;
          });
      }
    }
    
    (async () => {
      const apiWrapper = new ApiWrapper();
      const singleItem: string = await apiWrapper.request<string>("asdfsd");
      console.log(singleItem.endsWith);
    
      const arrayItemWrong: string[] = await apiWrapper.request<string>("asdfsd");
      const arrayItem: string[] = await apiWrapper.request<string[]>("asdfsd");
      console.log(arrayItem.concat);
    })();
    

    Playground

    【讨论】:

    • 改变我对你的回答,因为这绝对是一个更优雅的解决方案。我已经更新了我的实现以符合您的建议,感谢您的回复,Mosh!
    • 很高兴听到:)
    猜你喜欢
    • 2018-11-03
    • 1970-01-01
    • 2016-09-20
    • 2021-06-05
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2018-02-10
    • 2021-10-17
    相关资源
    最近更新 更多