Typescript: Utilizing Index-Based Callback Parameters in Functions

I am currently working on creating a function that can handle specific data types ("resource") along with an array of arrays containing call objects and corresponding callbacks for when the calls finish ("handlers").

function useHandleResource<
  R extends ReturnType<typeof useApi>,
  H extends [ReturnType<typeof useCall>, ((data: R['data'], cp: H[number]['0']['callParam']) => R['data'])?][]
>(
  resource: R,
  handlers: H
)

The result obtained from "useApi" includes a "data" property of a generic type.
The outcome of "useCall" has a "callParam" property of a generic type.

When utilizing this function, I expect TypeScript to provide me with the correct types for the callback parameters.

useHandleResource(
  testResource(), // "data" is of type "string[]"
  [
    [
      testCall(), // "callParam" is of type "string",
      // expected types: "string[]" for data and "string" for cp derived from callParam in testCall
      (data, cp) => []
    ]
  ]
);

Although there are no errors thrown, the "cp" parameter in the callback becomes "unknown"...

My goal is to pass multiple handlers, each specifying the corresponding type for "cp".

[
  [
    testCall(), // "callParam" is of type "string",
    // expecting cp to be of type "string"
    (data, cp) => [cp]
  ],
  [
    otherTestCall(), // "callParam" is of type "number",
    // should have cp as type "number"
    (data, cp) => [cp.toString()]
  ]
]

Answer №1

If you're open to using a rest parameter, this solution can be achieved with a mapped type:

type HandlerMap<TData, T> =  {
  [P in keyof T]: [Call<T[P]>, (data: TData, v: T[P]) => any]
}

type Call<T> = { callParam: T }
declare function useHandleResource<
  TData extends { data: any }, 
  TCallParam extends any[]
>(resources: TData, ...handlers: HandlerMap<TData['data'], TCallParam>): any


useHandleResource(
  { data: [""] },
  [
      { callParam: "" },
      (data, cp) => [cp] //cp string
  ],
  [
      { callParam: 0 },
      (data, cp) => [cp.toExponential()] //cp number
  ]
);

Playground Link

Edit

Realized that adding a constraint to make T a tuple will also support regular arrays:

type HandlerMap<TData, T> =  {
  [P in keyof T]: [Call<T[P]>, (data: TData, v: T[P]) => any]
}

type Call<T> = { callParam: T }
declare function useHandleResource<
  TData extends { data: any }, 
  TCallParam extends [any] | any[]
>(resources: TData, handlers: HandlerMap<TData['data'], TCallParam>): any


useHandleResource(
  { data: [""] },[
  [
      { callParam: "" },
      (data, cp) => [cp] //cp string
  ],
  [
      { callParam: 0 },
      (data, cp) => [cp.toExponential()] //cp number
  ]
  ]
);

Playground Link

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

The query fails to retrieve results for the specified date and the beginning of the month

I have encountered an issue with my query that is supposed to fetch values between the start and end of the month. Interestingly, when a record is entered on the first day of the month, it doesn't get returned in the query result. Only records entered ...

Getting the mssql output in Protractor using VSCode

I recently tried running the code below and it seems like the connection is established successfully. However, I'm unable to see any output. Is there a way to view the query result? I am still learning about Protractor, NodeJS, and MSSQL connections. ...

The database did not respond, causing the API to resolve without sending a response for the specified endpoint (/api/dates). This could potentially lead to requests becoming stalled in Next

I have been attempting to retrieve a list of dates from my database in a straightforward manner, but unfortunately, I am not receiving any response (even after trying to log the response data to the console). The only feedback I seem to be getting when sel ...

How to retrieve the default type returned by a function using a custom TypeMap

I have a function that returns a promise with a type provided by generics. const api = <Model>(url: string) => Promise<Model> Currently, I always need to set the type of the returned data. const data = await api<{id: string, name: string ...

NextJS Typescript Player

Encountering an issue during the build process, specifically with the error in audioRef.current.play(). The error message indicates that there is no play function available. I attempted to use an interface but it seems to not accept boolean values. Could ...

Is Typescript capable of converting ES6 code to ES5 during transpilation?

Currently, I'm in the process of developing an application utilizing Angular 2 and TypeScript. My goal is to incorporate a JavaScript method, specifically 'filter' for arrays, that is compatible with IE 11+, Chrome 45+, and other similar bro ...

The Angular 4 HTTP patch method is encountering difficulties when called in code but functions properly when tested using Post

My attempts to make a patch and post call to the server are failing as it never reaches the server. Interestingly, the same request works flawlessly in Postman, so I suspect there might be an issue with my code. Both my post and patch methods are essentia ...

Delay the Ngrx effect by 2 seconds before initiating the redirect

I have an ngrx effect that involves calling an HTTP method and then waiting for 2 seconds before redirecting to another page. However, the current behavior is that it redirects immediately without waiting. confirmRegistration$ = createEffect(() => { ...

Verify in Typescript if there is at least one value supplied

Looking for a solution: function takeOneOfOrThrow(firstOptionalVariable : string | undefined, secondOptionalVariable : string | undefined) { let result : string; if (!firstOptionalVariable && !secondOptionalVariable) { throw new E ...

Throw TypeError: The `pipe` property of `ngrx/store` is undefined during testing

Here is the code snippet from my TypeScript file: this.store.pipe(select(subscribe.getRegCategories)).pipe(takeUntil(this.ngUnsubscribe)).subscribe(data => { if (data && data.length) { this.allRegCategories = data; ...

Using Angular NgRx - triggering an action from an effect once certain actions yield a result

I'm encountering difficulties in dispatching actions that require results from five other actions (all listed in my effect). Could someone lend a hand? Essentially, I need to trigger an action within the effect only after these five actions have retu ...

The issue of excessive recursion in Typescript

Currently, I am in the process of learning Typescript while working on some exercises. While attempting to solve a particular problem, I encountered an error related to excessive recursion. This issue arises even though I created wrapper functions. About ...

Avoid stopping Bootstrap Vue's events

Need help with a b-form-select control in Bootstrap Vue. Trying to execute a function when the value changes, but want the option to cancel the event and keep the original value. Complicating matters, the select is in a child component while the function ...

You are unable to link to <custom directive selector> because it is not recognized as a valid property of 'div'

I am currently working on a project in StackBlitz, and you can find the link here: https://stackblitz.com/edit/angular-fxfo3f?file=src/directives/smooth-height.directive.ts I encountered an issue: Error in src/components/parent/parent.component.html (2:6) ...

Dividing component files using TypeScript

Our team has embarked on a new project using Vue 3 for the front end. We have opted to incorporate TypeScript and are interested in implementing a file-separation approach. While researching, we came across this article in the documentation which provides ...

hiding html elements by using the display property set to none instead of physically removing

I am currently utilizing an if-else statement to display different HTML structures. As a result, the entire HTML is being rendered twice. Is there a way we can utilize 'display: none' instead? I attempted to use it in th ...

Looking for a solution to the TypeScript & Mantine issue of DateValue not being assignable?

The required project dependencies for this task are outlined below: "dependencies": { "@mantine/core": "^7.6.2", "@mantine/dates": "^7.6.2", "@mantine/form": "^7.6.2", &q ...

Translating from a higher-level programming language to a lower-level programming language

Is compilation effectively the transformation of high-level programming languages (HLL) into machine code or low-level language? If so, why is TypeScript (a HLL) compiled to JavaScript (also a HLL) instead of being compiled to a low-level language? ...

Modify visibility within a subclass

Is there a way to modify property visibility in a child class from protected to public? Consider the following code snippet: class BaseFoo { protected foo; } class Foo extends BaseFoo { foo = 1; } new Foo().foo; It seems that this change is pos ...

Unable to append item to document object model

Within my component, I have a snippet of code: isLoaded($event) { console.log($event); this.visible = $event; console.log(this.visible); this.onClick(); } onClick() { this.listImage = this.imageService.getImage(); let span = docu ...