Function type that applies the function name and its arguments

Need help with properly typing a function that takes the name of a function and its arguments, applies the function, and returns the result. Here is the code snippet:

const sum = (a: number, b: number) => a + b
const concat = (a: string, b: string, c: string) => a + b + c

const funs = {
    sum,
    concat
}

type Keys = 'sum' | 'concat'

type Args<T> = T extends (...args: infer R) => any ? R : never

type Sum = Args<typeof sum>
type Concat = Args<typeof concat>

function apply<K extends Keys>(funKey: K, ...args: Args<typeof funs[K]>) {
    // encountering error 'An argument for 'a' was not provided.'
    return funs[funKey](...args)
}

const test1 = apply('sum', 1, 2)
const test2 = apply('concat', 'str1', 'str2', 'str3' )

Trying to resolve the error 'An argument for 'a' was not provided.' inside the apply function. Any suggestions?

Link to playgound

Answer №1

The compiler struggles with understanding type safety in situations where generic type parameters are not yet specified. This issue is detailed in a GitHub problem report at microsoft/TypeScript#24085.

There is a rare chance that the function might infer K as Keys itself instead of specific values like "sum" or "concat". For example:

const oops = apply(Math.random() < 0.5 ? "sum" : "concat", "a", "b", "c");
console.log(oops); // Result can be "abc" or "ab"

In this scenario, the compiler technically points out that the operation is not entirely type safe. To resolve this limitation, there is a feature proposal in microsoft/TypeScript#27808.

Furthermore, the compiler does not view the funKey parameter and the args rest parameter as having interconnected types. Even if it could, maintaining that correlation proves to be challenging.

Since the compiler may struggle to compute the return type on its own, manual annotations become necessary. The ReturnType<F> utility type comes in handy for this purpose. Additionally, you can utilize the Parameters<F> utility type instead of creating Args<F> from scratch.


To tackle these limitations, users often need to assure the compiler about the type safety of their operations due to the compiler's inability to verify it. A potential solution involves using a type assertion, such as any:

type Funs = typeof funs;

function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
    return (funs[funKey] as any)(...args);
}

This approach enables unconventional operations like

return (funs[funKey] as any)(true)
, highlighting the importance of cautious usage. Another, slightly more complex method involves representing funs[funKey] as a function that can handle various argument types and return multiple possible result types:

type WidenFunc<T> = ((x: T) => void) extends ((x: (...args: infer A) => infer R) => any) ?
    (...args: A) => R : never;

function apply<K extends Keys>(funKey: K, ...args: Parameters<Funs[K]>): ReturnType<Funs[K]> {
    return (funs[funKey] as WidenFunc<Funs[Keys]>)(...args);
}

The resultant function type WidenFunc<Funs[Keys]> becomes

(...args: [number, number] | [string, string, string]) => number & string
. While seemingly nonsensical, this type prevents incorrect arguments like (true) from being passed.


Either of these methods should suffice:

const test1 = apply('sum', 1, 2) // number
const test2 = apply('concat', 'str1', 'str2', 'str3') // string

Hopefully, this information proves helpful. Good luck!

Playground link to code

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

Angular (4, 5, 6, 7) - An easy guide to implementing slide in and out animations using ngIf

How can you implement a basic sliding animation in Angular4 to show and hide a container element? For example: <div *ngIf="show"> <!-- Content --> </div> Slide the content in (similar to jQuery's slideDown() method) from top t ...

The error message indicates a change in the binding value within the template, resulting in an

This is the structure of my component A : <nb-tab tabTitle="Photos" [badgeText]="centerPictures?.pictures?.length" badgePosition="top right" badgeStatus="info"> <app-center-pictures #centerPictures [center]="center"> ...

Substitute the specific class title with the present class

Here is a sample class (supposed to be immutable): class A { normalMethod1(): A{ const instance = this.staticMethod1(); return instance; } static staticMethod1: A(){ return new this(); } } The code above works fine, but how can I re ...

Error encountered: TypeScript/Knex - The import statement can only be used inside a module

I am new to TypeScript and currently utilizing Knex to construct a template table in our PostgreSQL database. I keep encountering the same issue with TypeScript on this file and others, here is the code snippet: import * as Knex from 'knex'; ex ...

Challenges encountered while integrating the Ngrx Store service with my component

Following a user's attempt to address an issue related to my post on Stackoverflow titled "My stackoverflow old post", I am currently working on implementing the Ngrx store using the guidelines provided on Ngrx store github. This will assist me in han ...

Transform the object into a date once it initiates at 1

After integrating the angular bootstrap datepicker component into my application, I encountered an issue with converting the selected date object {year: 1993, month: 8, day: 9} into a Date object. The conversion is successful, but the resulting date is shi ...

Angular UI validation malfunctioning upon loading of the page

My webpage contains multiple rows with specific validation requirements - at least one Key, Time, and Input must be selected. Initially, the validation works as expected. However, after saving and reloading the page, the default selection for Key, Time, an ...

Filtering two distinct arrays in Angular 6 to eliminate duplicate data: A quick guide

array1 = ["one","two"]; array2 = [ {"name":"one","id":101} , {"name":"two","id":102} , {"name":"three","id":103} , {"name":"four","id":104} ]; The data above consists of two arrays: array1 which contains string values, and array2 which contains objects. ...

What is the best method for managing an event loop during nested or recursive calculations?

When it comes to breaking a computation and releasing using setTimeout(), most examples seen involve having a shallow call stack. But what about scenarios where the computation is deeply nested or mutually-recursive, like in a tree search, with plenty of c ...

Mocking a Class Dependency in Jest

I have 4 classes available, and I utilize 3 of them to create an instance of another class. This is how it's done: const repo = new PaymentMessageRepository(); const gorepo = new GoMessageRepository(); const sqsm = new PaymentMessageQueueManager(pr ...

encountering a loading issue with angular2-modal in rc1 version

Currently, I am implementing a library called angular2-modal. This library offers various modal options including bootstrap and vex for angular 2. While vex is functioning properly, I encounter an error when switching to bootstrap: responsive-applicati ...

What causes the generation of an outdated object when utilizing the let and new keywords to create a new object within a service function?

Hey there, looking for a basic auth authentication service in angular2? Here's the issue I'm facing: When a user logs in for the first time, everything works smoothly. However, if they try to log in with a different account for the second time, ...

When testing my POST request online, it functions properly. However, I am facing difficulties in getting it to work in next.js as I keep receiving a 405

I am currently working on establishing a connection to Zoho Creator in order to retrieve some data. After successfully testing the request on and receiving the correct response, I encountered an issue while trying to implement it within a next.js applicat ...

Getting stuck in the loop: Angular 11 exceeding the maximum call stack size error

Recently encountered an issue where I forgot to import certain modules, resulting in the error "Error: Maximum call stack size exceeded." Despite attempting to import the project into StackBlitz, no solution was found. In the app.module.ts file: import { ...

Properties in a model class in typescript that share common characteristics

I need a data model class that can store identical information for two individuals simultaneously. For instance: export class People { constructor( public person1Name: string = '', public person1Age: number = 0, public ...

TypeScript implements strong typing for object strings

Is it possible to create a custom type in TypeScript that represents a stringified object with a specific structure? Instead of just defining type SomeType = string, I would like to define a type that looks something like this: type SomeType = Stringified ...

What is the process for integrating unit tests from external sources into an Angular project following an upgrade to version 15

As of Angular v15, the require.context function has been removed from the test.ts configuration file. I used to rely on require.context to expose tests outside of the Angular project to Karma. Now that it's no longer available: const contextGlobal = ...

Preventing click propagation for custom react components nested within a MapContainer

I have developed a custom control React component for a map as shown below: export const MapZoom = () => { const map = useMap() const handleButtonClick = () => { map.zoomIn() } return ( <IconButton aria ...

Was anticipating 1 argument, however received 5 in TypeScript

When running the code in this part, I expected to receive 0-1 arguments but ended up getting 5 instead. Do you have any suggestions for a solution? Register() { let newUser = new User(this.registerForm.value, newUser.city =this.cityid, ...

Contrast the differences between arrays and inserting data into specific index positions

In this scenario, I have two arrays structured as follows: arr1=[{room_no:1,bed_no:'1A'}, {room_no:1,bed_no:'1B'}, {room_no:2,bed_no:'2A'}, {room_no:3,bed_no:'3A'}, {room_no:3,bed_no:'3B ...