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.