The conundrum of nested function wrapping in Typescript and its impact on

Upon calling the following function, it returns a 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 an example of TypeScript code that replicates the issue. It is uncertain what is causing this problem, or if it is due to a TypeScript limitation. How can this typing issue be resolved?

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);



Answer №1

When it comes to the return type of Promise<any>, it all boils down to how Typecript's generic expects the type to be determined by the input parameters. In the case of PromiseFunction within res2, the return type is set as

Promise<UnwrappedReturnType<ApiFn>
. Here, the UnwrappedReturnType type specifically requires the return value of the PromiseFn type. Given that the ApiFn type extends the PromiseFn type, and the return value of PromiseFn is Promise<any>, it follows that the type UnwrappedReturnType<ApiFn> is essentially any.

In simpler terms, by specifying the generic type as ApiFn, it enables the type inference for res2.

...
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);

If the intended function is still unclear, feel free to provide more context, and I can suggest alternative approaches.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Subpar resolution of PNG images displayed in HTML canvas

My attempt to draw a PNG image onto the canvas is resulting in poor quality. I am using the drawImage method as shown below: src = folder+self.cur+".png"; imageObj.src = src; imageObj.onload = function() { context.clearRect(0, 0, cv, ch), context.drawImag ...

When attempting to retrieve a data object using a Vuex action, I encounter an "Uncaught (in promise) TypeError" when trying to access the data within the object

Recently, I've been diving into Vuex and its actions for fetching data. While everything seems to be working smoothly - accessing the films object, selecting films from it - I encounter an error when trying to access specific data within a film. Vuex ...

What is the best way to rearrange (exchange) elements within an Immutable Map?

Is there a way to rearrange items within an unchangeable list that is part of a Map? Here's an example: const Map = Immutable.fromJS({ name:'lolo', ids:[3,4,5] }); I have attempted to use the splice method for swapping, as well as ...

What is the process for overriding the module declaration for `*.svg` in Next.js?

The recent modification in Next.js (v11.0.x) has introduced new type definitions: For next-env.d.ts (regenerated at every build and not modifiable): /// <reference types="next" /> /// <reference types="next/types/global" /> ...

Creating a functional hyperlink within a ui-sref element in Ionic

I'm struggling with a simple code snippet in Ionic 1. It basically shows a list of items that are clickable to navigate to a details page. However, I want to add functionality so that when clicking on the phone icon next to each item, it will initiate ...

The file-loader in Webpack is failing to copy the files and update the URL

I am encountering an issue with webpack file loader where it is not outputting image files. This problem arises while also importing HTML files as partials in my Angular app. Below is my webpack configuration for handling images using the file-loader: [w ...

Encountering the 404 Page Not Found error upon refreshing the page while utilizing parallel routes

I'm currently developing a webapp dashboard using the latest version of Next.js 13 with app router. It features a dashboard and search bar at the top. I attempted to implement parallel routes. The @search folder contains the search bar and page.jsx wh ...

Prepare fixtures for commands in Cypress before executing the hook

One of my main tasks is to load the fixtures file only once and ensure it is accessible across all project files. To achieve this, I created a fixtures.js file with the following content: let fixturesData; export const loadFixturesData = () => { cy ...

Is the dragging behavior of a rotated image different than that of the original image when using CSS rotation?

While working on a CSS grid to showcase images rotated at 60 degrees for a diagonal view, I encountered an issue. I wanted users to have the ability to drag and drop images within the grid, but when they drag an image, it moves as if it weren't rotate ...

jQuery does not allow for text input values to be posted

Having an issue with a form using the POST method. I have some PHP controls that are being calculated in jQuery. While all the form control values are accessible in the next form using POST, the values added through jQuery are not posting to the next form. ...

WebView on Android still showing highlighted text even after selection has been cleared

When using my web app on an android WebView, I've noticed that whenever I click on something or navigate somewhere, a blue highlight appears on the container div. It sometimes disappears quickly, but other times it remains until clicking elsewhere. I ...

How to Monitor Store Changes within a Vue File Using Vue.js

I am working with 2 vue files, header.vue and sidebar.vue. Both components are imported into layout.vue. Here are the steps I am following: 1. Initially, when the page loads, I update the store in header.vue with some values inside the created hook. 2. ...

What is the best way to notify administrator users when their accounts have exceeded the timeout period?

Working on the website for our high school newspaper, I've encountered a recurring issue on the admin page. Users are getting logged out after creating an article due to a time limit constraint. To address this problem, my goal is to implement an aler ...

What is the best way to export several 'sub-components' from a single node.js module?

Here's what I mean by exporting 'sub-modules': var fibers = require('fibers'); // this is functional as the 'main' module var future = require('fibers/future'); // also operational as a 'sub' ...

Using the spread operator in Typescript with an object that contains methods

Within my Angular project, I am faced with an object that includes a type and status field where the status can change depending on the type. While some might argue that this is not the best design practice, it is how things are currently structured in my ...

How can I change the text of a single button by clicking on it without affecting the other buttons that share the same

Currently, I am implementing a posting system where posts can be added using a button. The posts have two additional buttons - one to show/hide content and the other to delete content. I am utilizing jQuery's slideToggle event to toggle the visibility ...

Guide to switching from test mode to live mode and enabling live mode in stripe with nodejs

I have encountered an issue with the stripe form I am currently using for payments. When the form is loading, it displays "test mode" in the top right corner. I am unsure how to switch it to live mode and cannot find any option on the stripe dashboard to d ...

Utilize regular expressions to substitute content with HTML tags within a directive

While working with Angular JS to iterate through Twitter tweets using ng-repeat, I encountered the need to highlight certain parts of the tweet string such as @tag and #hash. To achieve this, it was suggested to utilize replace method to wrap these element ...

Framer Motion causes a crash in a Next.js application with the error message: "Unable to find named export 'useId'"

I am encountering an error in my Next.js app that I can't seem to trace back to its source. Strangely, the code triggering the error is not something I wrote myself. error - file:///Users/cheq/Desktop/cheqo/node_modules/framer-motion/dist/es/component ...

Issue with JQuery: Inability to deactivate an element after receiving an Ajax response

My dynamic dialogue box, generated via Ajax return, presents a challenge involving the dynamically changing drop-down list element $('#functionSelect'). I require this list to trigger disabling of input fields within the dialogue box upon changes ...