问题
Here I have a function that works well for catching sync errors, and doing something with them before re-throwing them.
function logExceptions<T extends (...args: any[]) => any>(func: T): (...funcArgs: Parameters<T>) => ReturnType<T> {
return (...args: Parameters<T>): ReturnType<T> => {
try {
return func(...args);
} catch (err) {
console.log(func.name + " caused an error")
throw err;
}
};
}
function syncExample() {
throw new Error()
}
logExceptions(syncExample)();
console.log
"syncExample caused an error"
Can I rewrite this function to become async agnostic and also work for async functions?
async function asyncExample() {
throw new Error()
}
logExceptions(asyncExample)();
desired console.log
"asyncExample caused an error"
actual console.log
""
回答1:
Can I rewrite this function to become async agnostic and also work for async functions?
No. While you could try to overload it and detect whether the function would return a promise or not, that's very fragile. Better write a separate function for wrapping asynchronous functions:
function logExceptions<T extends any[], U>(func: (...args: T) => PromiseLike<U>): (...args: T) => Promise<U> {
return async (...args) => {
try {
return await func(...args);
} catch (err) {
console.log(func.name + " caused an error")
throw err;
}
};
}
回答2:
Agree with @Bergi about the new function.
Typescript doesn't like it when we return directly the ReturnType
in case of async
method use. I guess it's because I havent specified that ReturnType
must be of type Promise
, but I've found now way how to specify it.
type ReturnType any> = T extends (...args: any) => infer R ? R : any Obtain the return type of a function type
The return type of an async function or method must be the global Promise type.(1064)
I've found a turnaround by extracting what's templated inside of the Promise
and redeclaring it.
type ExtractPromiseTemplate<T> = T extends PromiseLike<infer U> ? U : T
function logExceptions<T extends (...args: any[]) => ReturnType<T>>(func: T): (...funcArgs: Parameters<T>) => Promise<ExtractPromiseTemplate<ReturnType<T>>> {
return async (...args: Parameters<T>): Promise<ExtractPromiseTemplate<ReturnType<T>>> => {
try {
console.log('Will call now');
const ret = await func(...args);
return ret as ExtractPromiseTemplate<ReturnType<T>>;
} catch (err) {
console.log(func.name + " caused an error");
throw err;
}
};
}
async function asyncExample() {
throw new Error('Example')
}
logExceptions(asyncExample)();
Call the following code to test the validity of the returned value :
type ExtractPromiseTemplate<T> = T extends PromiseLike<infer U> ? U : T
function logExceptions<T extends (...args: any[]) => ReturnType<T>>(func: T): (...funcArgs: Parameters<T>) => Promise<ExtractPromiseTemplate<ReturnType<T>>> {
return async (...args: Parameters<T>): Promise<ExtractPromiseTemplate<ReturnType<T>>> => {
try {
console.log('Will call now');
const ret = await func(...args);
return ret as Promise<ExtractPromiseTemplate<ReturnType<T>>>;
} catch (err) {
console.log(func.name + " caused an error");
throw err;
}
};
}
async function asyncExample():Promise<string> {
return 'a';
}
(async() => {
const ret = await logExceptions(asyncExample)();
})();
New playground for @bergi comment
来源:https://stackoverflow.com/questions/62325850/rewrite-error-catching-higher-order-function-to-catch-async-errors