Home > database >  Throwing errors in asynchronous functions that use type parameters
Throwing errors in asynchronous functions that use type parameters

Time:03-13

I'd like to be able to throw errors in an asynchronous function that uses type parameters. This satisfies all typing requirements:

function foo<T>(bar: T): Promise<T> {
    return new Promise(() => bar);
}

This does not:

function foo<T>(bar: T): Promise<T> {
    return new Promise(() => bar).catch((error) => throw new Error('Some error'));
}

The TypeScript error this causes is:

Type 'Promise<unknown>' is not assignable to type 'Promise<T>'.
  Type 'unknown' is not assignable to type 'T'.
    'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'.ts(2322)

Here's a real-world scenario for the above situation where I repackage Axios-specific HTTP errors as something vendor neutral:

async get<T>(path: string, query: Record<string, string>): Promise<HttpResponse<T>> {
    return this.instance
        .get(path, {
            params: query,
        })
        .then((response) => this.mapToHttpResponse(response))
        .catch((error) => throw new HttpError(error.response.status));
}

What's the proper way to throw errors in a function that uses type parameters?

CodePudding user response:

Solved it myself with a try/catch.

async get<T>(path: string, query: Record<string, string>): Promise<HttpResponse<T>> {
    try {
        return await this.instance
            .get(path, {
                params: query,
            })
            .then((response) => this.mapToHttpResponse(response));
    } catch (error) {
        throw new HttpError(error.response.status);
    }
}

CodePudding user response:

In the minimal reproduction you gave, you should specify the generic parameter inside of the Promise constructor:

function foo<T>(bar: T): Promise<T> {
    return new Promise<T>(() => bar).catch((error) => { throw new Error('Some error') });
}

TypeScript Playground Link

However, it is not as simple with the case of using axios. Here is what I would do with your given code. Note the HttpResponse is just a simple wrapper thing I created:

import axios from "axios";
import type { AxiosResponse } from "axios";

type HttpResponse<T> = { foo: T };

class MyHttpWrapper {

    instance = axios.create();

    mapToHttpResponse<T>(response: AxiosResponse<T>): HttpResponse<T> {
        return { foo: response.data };
    }

    async get<T>(path: string, query: Record<string, string>): Promise<HttpResponse<T>> {
        return this.instance
            .get(path, {
                params: query,
            })
            .then((response) => this.mapToHttpResponse(response))
            .catch((error) => { throw new Error(error.response.status) });
    }
}
  • Related