Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Fetch API return Promise<T> or Promise<T[]>

I'm writing an API wrapper in Typescript and I'm looking at using the Fetch API with Promises. I want to use generics in the request method so that I can model bind to the type provided however I'm fairly new to Typescript (and promises, for that matter) and cannot find anything on conditionally returning Promise<T> or Promise<T[]>, since not all requests will return an array.

This is the state of my code, so far, it's not in a finished state but it's workable.

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

I can try to conditionally return like so:

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

Although T is not assignable to type T[]. This may be really simple, silly or not even possible but any pointers would be appreciated. Any additional information can be provided if there's

like image 340
cossacksman Avatar asked Oct 27 '25 21:10

cossacksman


1 Answers

You don't need to return the same result with a different signature. T can be not only single type (such as string, number, etc.) but also an array of a type.

Also, I don't see where the function is void. If you meant to the case if throwing an error, the right signature is never.

So, the function can look as the follow:

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

like image 190
Mosh Feu Avatar answered Oct 30 '25 20:10

Mosh Feu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!