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

Unsuccessful invocation of React's componentDidMount method

Our UI designer created a Tabs component in React that allows for selecting and rendering child components based on index. However, I am facing an issue where the componentDidMount function is not being called when switching between tabs. I have implement ...

Error Encountered: Monorepo Shared Package Not Detected in Docker-Compose Execution

In my development setup, I have organized a monorepo using lerna and yarn workspaces. All code is written in typescript and then compiled into javascript. However, I encountered an issue with sharing packages when running the monorepo with docker-compose. ...

Error: The term 'makeStyles' cannot be found in the '@material-ui/core' module after installing Material-ui-pickers

Can anyone help me with using the inlineDatePicker components from Material UI pickers? I found them here: After running the npm -i command, I encountered an error during compilation: Failed to compile. ./node_modules/material-ui-pickers/dist/material-u ...

Unable to adjust the text color to white

As someone who is new to Typescript, I'm facing a challenge in changing the text color to white and struggling to find a solution. I'm hoping that someone can guide me in the right direction as I've tried multiple approaches without success ...

The Type {children: Element; } is distinct and does not share any properties with type IntrinsicAttributes

I am encountering an issue in my React application where I am unable to nest components within other components. The error is occurring in both the Header component and the Search component. Specifically, I am receiving the following error in the Header co ...

What is the best way to refresh existing data retrieved by React Query without having to fetch everything again?

My current code structure requires me to refetch all the data after a successful mutation, as the client-side tasks are not updated automatically. Is there a way to update the tasks directly when I create or delete a task? const { data: sessionData } = ...

"Although TypeOrm successfully generates the database, there seems to be a connectivity issue

Attempting to set up a JWT authentication system using NestJs and SQLite. The code successfully generates the SQLite file, but then throws an error stating "Unable to connect to the database." Upon checking with the SQLite terminal, it became apparent that ...

Incorporating OpenLayers and TypeScript: Issue with Event.map.forEachFeatureAtPixel, where the argument type is not compatible with the parameter type

I am currently attempting to implement Open Layers v7.2.2 with TypeScript. {When not using TypeScript, the code functions properly} function OnMapClick(event: MapBrowserEvent<UIEvent>) { event.map.forEachFeatureAtPixel(event.pixel, function(curren ...

The current date is cycling back to the month before

There is a datetime received from my api as 2018-09-01T00:00:00.000Z, referred to as frame.scandate. Another date is generated within the program as 2018-09, simply known as scandate. These examples can represent any year/month combination. In my code: ...

Unexpected Typescript error when React component receives props

I encountered an unexpected error saying ": expected." Could it be related to how I'm setting up props for the onChange event? Here is my code for the component: import React from "react"; interface TextFieldProps { label?: string; ...

Getting exported members through Typescript Compiler API can be achieved by following these steps:

I am currently working on a project hosted at https://github.com/GooGee/Code-Builder This particular file is being loaded by the Typescript Compiler API: import * as fs from 'fs' Below is a snippet of my code: function getExportList(node: t ...

Customize YouTube iframe styles in Angular 4+ with TypeScript

Has anyone been successful in overriding the style of an embedded YouTube iframe using Angular 4+ with TypeScript? I've attempted to override a CSS class of the embed iframe, but have not had any luck. Here is the URL to YouTube's stylesheet: ...

Encountering difficulty using a template file as a component template within the Liferay angular portlet

Encountering trouble using a template file as a template for the component in my Liferay angular portlet. It works fine with a regular Angular application. app.component.ts import { Component } from '@angular/core'; @Component({ templateUr ...

Reversing ngModel modifications does not accurately display changes in the view

Presently, my table contains editable cells, with the functionality to undo changes to each cell. To achieve this, I initially created a duplicate of each object in the array. Upon initialization, I mapped the array to create a new array with old values s ...

Differences between RxJs Observable<string> and Observable<string[]>

I'm struggling to grasp the concept of RxJS Observables, even though I have utilized the observable pattern in various scenarios in my life. Below is a snippet of code that showcases my confusion: const observable: Observable<Response> = cr ...

Utilize mapping to object and preserve type inference

I am currently developing a function that utilizes a map function to map objects. interface Dictionary<T> { [key: string]: T; } function objectMap<TValue, TResult>( obj: Dictionary<TValue>, valSelector: (val: TValue) => TResult ...

Disabling the use of console.log() in a live environment

In an effort to disable console logs for production environments in my angular application, I implemented the code below. While it successfully suppresses logs in Chrome, IE 11 continues to display them. Here is the snippet from main.ts: if (environment. ...

Modify the selected toggle buttons' color by utilizing the MUI ThemeProvider

I am currently working on customizing the color of selected toggle buttons within my React app using TypeScript and ThemeProvider from @mui/material 5.11.13. Despite my efforts, when a toggle button is selected, it still retains the default color #1976d2, ...

How to iterate through properties declared in an Interface in Angular 12?

Before Angular 12, this functioned properly: export interface Content { categories: string[] concepts: Topic[] formulas: Topic[] guides: Topic[] } //this.content is of type Content ['formulas', 'concepts'].forEach(c =&g ...

Encountering the error message "The argument type 'AsyncThunkAction<*>' cannot be assigned to the parameter type 'Action<any>'" while trying to utilize a custom typed useAppDispatch hook

For a live example, you can check out this link. In the process of developing a React application with TypeScript and Redux Toolkit, I have meticulously followed the guidelines outlined in the documentation. As a result, I have successfully implemented ty ...