Home > Back-end >  Typescript nested function wrapping Return type problem
Typescript nested function wrapping Return type problem

Time:03-24

I am calling the below function which returns me Promise<boolean>

const fnc = (i:number) : Promise<boolean>  => Promise.resolve(true)

// Promise<boolean>
const res1 = errorHandler(errorPredicates.sdkError1, fnc, null, 4);

However, when I nest it with another error handler the returned object changes to Promise<any>

// Promise<any>
const res2 = errorHandler(errorPredicates.sdkError1, errorHandler, null, errorPredicates.sdkError2, fnc, null, 4);

Below is the example typescript code which reproduces the issue. I am not sure what is wrong here or if it is a limitation of typescript. How can I fix this typing issue?

type PromiseFn = (...args: any[]) => Promise<any>;
type UnwrappedReturnType<T extends PromiseFn> = T extends (...args: any) => Promise<infer R> ? R : any

type ThrottlerPredicate = (err: any) => boolean;

type ThrottlerPredicates = {
    sdkError1: ThrottlerPredicate
    sdkError2: ThrottlerPredicate
}

const errorPredicates: ThrottlerPredicates = {
    sdkError1: (err) => err?.message?.search(`429:`) != -1,
    sdkError2: (err) => err?.message?.search(`Request was throttled.`) != -1
}

async function errorHandler<ApiFn extends PromiseFn>(
    predicate: ThrottlerPredicate,
    promiseFunction: ApiFn,
    thisArg: ThisParameterType<ApiFn>,
    ...args: Parameters<ApiFn>
): Promise<UnwrappedReturnType<ApiFn>> {
    let errCount = 0
    do {
        try {
            const promiseResult = await promiseFunction.call(thisArg, ...args)
            return promiseResult
        } catch (err: any) {
            //console.error(err)
            if (predicate(err)) {
                if (errCount < 20)
                      errCount;
                var ms = 1500 * errCount
            } else
                throw (err);
        }
    }
    while (true);
}

const fnc = (i:number) : Promise<boolean>  => Promise.resolve(true)

// Promise<boolean>
const res1 = errorHandler(errorPredicates.sdkError1, fnc, null, 4);

// Promise<any>
const res2 = errorHandler(errorPredicates.sdkError1, errorHandler, null, errorPredicates.sdkError2, fnc, null, 4);



CodePudding user response:

The reason why the return type is Promise<any> is that the generic of Typecript expects the type based on the parameters entered. Therefore, the return type of PromiseFunction in res2 is Promise<UnwrappedReturnType<ApiFn>. The UnwrappedReturnType type expects the PromiseFn type return value. At this time, the ApiFn type is an extends of PromiseFn, and the PromiseFn type's return value is Promise<any>, so the UnwrappedReturnType<ApiFn> type is any. Again, the errorHandler generic ApiFn type used as a parameter is the same as PromiseFn((...args: any[]) => Promise<any>) type because there are no parameters expected.

In other words, if you specify the ApiFn generic type, res2 type inference is possible.

...
type ErrorHandler<ApiFn extends PromiseFn> = (
  predicate: ThrottlerPredicate,
  promiseFunction: ApiFn,
  thisArg: ThisParameterType<ApiFn>,
  ...args: Parameters<ApiFn>
) => Promise<UnwrappedReturnType<ApiFn>>;

// Promise<boolean>
const res2 = errorHandler(errorPredicates.sdkError1, errorHandler as ErrorHandler<typeof fnc>, null, errorPredicates.sdkError2, fnc, null, 4);

Unfortunately, I didn't understand the function you wanted, so I answered based on your question. If you can explain the logic to me, I'll think about a better way.

  • Related