Determine the type of embedded function by analyzing the callback

Are you struggling to create a function that takes an object and returns a nested function, which in turn accepts a callback and should return the same type of function? It seems like achieving this with the same type as the callback is posing quite a challenge.

type Constructor<T> = new (...args: any[]) => T


export type Settings = {
   maxCalls: number
   interval: number
   errors: Constructor<Error>[]
}

export function withRetryDelayed<R, U extends any[]>({
   maxCalls = 2,
   interval = 100,
   errors = [Error]
}: Partial<Settings> = {}): (cb: (...args: U) => Promise<R>) => (...args: U) => Promise<R> {
   let calls = maxCalls
   return (callback: (...args: U) => Promise<R>): (...args: U) => Promise<R> => {
      const retrying = async (...args: U): Promise<R> => {
         try {
            return await callback(...args)
         } catch (err) {
            if (calls-- <= 1 || !errors.some(ErrorConstructor => err instanceof ErrorConstructor)) {
               throw err
            }
            return interval
               ? new Promise(resolve => {
                  setTimeout(() => resolve(retrying(...args)), (maxCalls - calls) * interval)
               })
               : retrying(...args)
         }
      }
      return retrying
   }
}

const theX = (a: string): Promise<string> => Promise.resolve(a)

class MockError1 extends Error { }



const withRetryCallback = withRetryDelayed({
   maxCalls: 10,
   errors: [MockError1]
})(theX)


// typeof withRetryCallback is (...args: any[]) => Promise<unknown>

Playground

Answer №1

When implementing generics, it's best to apply them at the inner function level rather than the outer function level. By doing this, the resulting function will be generic and its type can be inferred based on the provided argument.


type Constructor<T> = new (...args: any[]) => T


export type Configurations = {
   maxAttempts: number
   intervalTime: number
   errors: Constructor<Error>[]
}

export function implementRetryLogic({
   maxAttempts = 2,
   intervalTime = 100,
   errors = [Error]
}: Partial<Configurations> = {}) {
   let attempts = maxAttempts
   return <R, U extends any[]>(functionToRetry: (...args: U) => Promise<R>): (...args: U) => Promise<R> => {
      const retryingFunction = async (...args: U): Promise<R> => {
         try {
            return await functionToRetry(...args)
         } catch (err) {
            if (attempts-- <= 1 || !errors.some(ErrorConstructor => err instanceof ErrorConstructor)) {
               throw err
            }
            return intervalTime
               ? new Promise(resolve => {
                  setTimeout(() => resolve(retryingFunction(...args)), (maxAttempts - attempts) * intervalTime)
               })
               : retryingFunction(...args)
         }
      }
      return retryingFunction
   }
}

const exampleFunction = (input: string): Promise<string> => Promise.resolve(input)

class CustomError extends Error { }

const retryLogicImplementation = implementRetryLogic({
   maxAttempts: 10,
   errors: [CustomError]
})(example)

// The typeof retryLogicImplementation is now (input: string) => Promise<string>

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

Schedule - the information list is not visible on the calendar

I am trying to create a timeline that loads all data and events from a datasource. I have been using a dev extreme component for this purpose, but unfortunately, the events are not displaying on the calendar. Can anyone offer any insights into what I might ...

Utilizing a dictionary for comparing with an API response in order to generate an array of unique objects by eliminating duplicates

I currently have a React component that utilizes a dictionary to compare against an API response for address state. The goal is to map only the states that are returned back as options in a dropdown. Below is the mapping function used to create an array o ...

NuxtJS (Vue) loop displaying inaccurate information

I have a dataset that includes multiple languages and their corresponding pages. export const myData = [ { id: 1, lang: "it", items: [ { id: 1, title: "IT Page1", }, { ...

The use of dates (YYYY-MM-DD) as identifiers in HTML/Angular 2 is causing issues

I have successfully created a calendar using html in Angular 2, and now I am looking to incorporate events into it. These events should be able to color specific days, add a dot inside the cell, or something similar. I have a json file that contains the ev ...

Retrieve the current step index in Angular Material Design Stepper

In my endeavors to retrieve the selected step within a component utilizing Angular Material Design stepper, I am encountering some issues. My current approach involves using the selectedIndex property, but it consistently returns "1" instead of the desire ...

Using useRef as a prop in React with TypeScript

I am currently experimenting with TypeScript and encountering an issue when trying to use useRef in a custom element specifically when passing it as a prop I have attempted the following: import React from "react"; export interface InputProps extends ...

Tips on updating checkbox background color after being selected

I've been working on creating checkboxes for seat selection in Angular using a TypeScript file, but I'm facing an issue where the background color of the checkbox doesn't change after checking them. Here's my TypeScript function: gener ...

Apollo useQuery enables risky array destructuring of a tuple element containing any value

Currently, I am incorporating TypeScript into my project and have a GraphQL query definition that utilizes Apollo's useQuery. According to the documentation, the call should be typed, however, I am encountering an ESLint error regarding data being ass ...

Choose a single asset from the list of values stored in the Map

I'm looking to implement something similar to the following: let myMap = new Map<string, any>(); myMap.set("aaa", {a: 1, b: 2, c:3}); myMap.set("bbb", {a: 1, b: 2, c:6}); myMap.set("ccc", {a: 1, b: 2, c:9}); let cs = myMap.values().map(x => ...

Unique component for customized form controls

Currently, I am delving into the world of Angular. One challenge I have been tackling is creating a custom form control that includes a mat-select. The goal is for the submitted form value to be the item selected in the dropdown. After sifting through num ...

What is preventing the getters for form errors from functioning in my Angular Reactive Form component template, while a direct reference does work?

I have a question regarding a small issue with an Angular reactive form in a lazily loaded signup module. The code structure is structured as follows: TS get email() { return this.myForm.get('email'); } // While this code works from within th ...

Using path aliases in a Typescript project with Typescript + Jest is not a viable option

Note I am able to use aliases in my TypeScript file. Unfortunately, I cannot use aliases in my test files (*.test.ts). My Configuration 1. Jest.config.ts import type { Config } from '@jest/types'; const config: Config.InitialOptions = { ve ...

Steps to activate zone-conscious bluebird assurances in Angular 8

In order to make all the promises in my Angular 8 project cancelable, I embarked on a quest to find the perfect library for the job. It was during this search that I stumbled upon bluebird.js, a promising candidate ;-) Following these guidelines on integr ...

Why is it necessary to merge an interface with a function when using the new keyword?

Assuming that the setting noImplicityAny is enabled. Consider the following: function Bar() { this.f = 100; } The following code snippet will not function as expected: let x = new Bar(); An error message will be displayed: 'new' expre ...

What is the best way to assign JSON data to a Class variable within Angular?

In my code, I have a class called Projects export class Projects { project_id: number; project_name: string; category_id: number; project_type: string; start_date: Date; completion_date: Date; working_status: string; project_info: string; area: string; add ...

Creating PropTypes from TypeScript

Currently in my React project, I am utilizing TypeScript along with PropTypes to ensure type checking and validation of props. It feels redundant to write types for both TypeScript and PropTypes, especially when defining components like ListingsList: inte ...

Encountered an error with create-react-app and MaterialUI: Invalid hook call issue

I am encountering an issue while trying to set up Create-react-app with Material UI. The error message I receive pertains to Hooks. Could there be something else that I am missing? This is the specific error message being displayed: Error: Invalid hook ...

The IDE is showing an error, but Jest is able to run it flawlessly

I recently created a Jest unit test for a TypeScript function called checkEmail, which internally uses showAlert. The showAlert function in the utils.ts file looks like this: export const showAlert = (message: string) => { toast(message); }; In my ...

Having trouble setting the default value of a select element with 'selected' in Angular's bootstrap?

Click here I've been facing some difficulties in making the 'selected' tag work to pre-select my default select value. It seems like it might be related to the unique pipe I'm using and how Angular handles it. I have experimented with ...

Dynamically load components within a modal window

I am looking for a way to dynamically load a custom component inside a modal while keeping it as flexible as possible. Here is an example : -HTML CODE- <button id="floating_button" class="floating_button animation_floating_in" (click)="loadCustomComp ...