If I'm passing a typed function to Function.prototype.call
, is there a way to infer the return type from the passed function?
Below I would expect response to be typed based on someTypedFunction return type, but instead is unknown
. Are there generics for .call? Or would should I type it as any
and set the type of the response?
function getNewToken(): number {
return Math.floor(Math.random()*1000);
}
//Wrapper - Outside Parent Class
async function retryOnce<R>(this: any, fn: () => R): Promise<R> {
let isRetry = false;
const execute = async () => {
try{
return await fn();
}catch(error: any){
if(!isRetry){
isRetry = true;
this.token = getNewToken();
return execute();
}else{
throw Error();
}
}
}
return execute();
}
function someTypedFunction(option: string, token: number): Promise<string>{
return new Promise((resolve, reject) => {
if(token){
return resolve(option);
}
return reject("No token");
});
}
class Parent {
token;
constructor(token: number){
this.token = token;
}
async someMethod(){
//Usage - Inside Parent Class
const bar = () => someTypedFunction("variable", this.token);
const response = await retryOnce.call(this, bar);
return response;
}
}
call
is inferring its generic types from retryOnce
's which isn't inferring R
because it's not being directly called with bar
.
One thing you can do is have retyOnce
return a function with the desired functionality, that way you can call retryOnce(bar)
, and the returned function will have the correct return type, which call
can then infer correctly.
function getNewToken(): number {
return Math.floor(Math.random() * 1000);
}
//Wrapper - Outside Parent Class
function retryOnce<R>(fn: () => R): (this: Parent) => Promise<R> { // Return a new function
return async function () {
let isRetry = false;
const execute = async () => {
try {
return await fn();
} catch (error: any) {
if (!isRetry) {
isRetry = true;
this.token = getNewToken();
return execute();
} else {
throw Error();
}
}
}
return execute();
}
}
function someTypedFunction(option: string, token: number): Promise<string> {
return new Promise((resolve, reject) => {
if (token) {
return resolve(option);
}
return reject("No token");
});
}
class Parent {
token;
constructor(token: number) {
this.token = token;
}
async someMethod() {
//Usage - Inside Parent Class
const bar = () => someTypedFunction("variable", this.token);
const response = await retryOnce(bar).call(this);
// ^?const response: string
return response;
}
}