Typescript - The Power of Dynamic Typing

Currently, I am attempting to demonstrate this example => typescript playground

const obj = {
    func1: ({ a }: { a: string }) => { console.log(a) },
    func2: ({ b }: { b: number }) => { console.log(b) },
}

function execFunction<Key extends keyof typeof obj>(key: Key, params: Parameters<typeof obj[Key]>[0]) {
    const func: typeof obj[Key] = obj[key]

    return func(params)

}

execFunction('func1', { a: 'a' })
execFunction('func2', { b: 1 })

While using execFunction, the auto-completion feature works fine. However, I am encountering an error on func(params) and I am unsure why. Any thoughts?

Argument of type '{ a: string; } | { b: number; }' is not assignable to parameter of type '{ a: string; } & { b: number; }'.
  Type '{ a: string; }' is not assignable to type '{ a: string; } & { b: number; }'.
    Property 'b' is missing in type '{ a: string; }' but required in type '{ b: number; }'.(2345)
input.tsx(3, 22): 'b' is declared here.

I have tried to infer types, but I am unable to find a workaround for this issue...

Thank you for your assistance

Answer №1

When it comes to TypeScript, the compiler struggles to understand that the generic operation obj[key], where key is a generic type, will be a function accepting

Parameters<typeof obj[Key]>[0]
as an argument. The compiler sees the Parameters utility type as ambiguous and tends to treat it as an unknown type. This confusion leads to widening Key to a constraint of
"func1" | "func2"
, resulting in obj[key] being a union of functions, and params being a union of arguments. This scenario raises concerns about mismatched types and potential errors, even though in practice, it may not be an issue.

execFunction(Math.random() < 0.5 ? "func1" : "func2", { a: "" }); // no error!

In situations where you have a union of functions and a union of parameters, TypeScript faces challenges in correctly correlating them. The compiler is unable to recognize the relationship between func and params when both are of union types, which leads to errors. The recommended approach to resolving this issue involves using generics to index into a mapped type, ensuring type safety and allowing the function call with the appropriate parameters.


A concrete example that TypeScript understands involves defining explicit types for functions and their corresponding parameters:

interface Arg {
    func1: { a: string };
    func2: { b: number };
}

const obj: { [K in keyof Arg]: (arg: Arg[K]) => void } = {
    func1: ({ a }) => { console.log(a.toUpperCase()) },
    func2: ({ b }) => { console.log(b.toFixed(1)) }
}

function execFunction<K extends keyof Arg>(key: K, params: Arg[K]) {
    const func = obj[key];
    return func(params); 
}

By using mapped types and generics, you can simplify the code and help the compiler understand the relationships between functions and their parameters. This structured approach improves clarity and type safety in your TypeScript code.

For additional insights and detailed explanations on handling correlated unions in TypeScript, refer to resources such as microsoft/TypeScript#30581 and microsoft/TypeScript#47109.


Considering the limitations of the original code, a recommended modification involves using generics to verify type relationships and ensure smooth function calls with the correct parameters:

function execFunction<K extends keyof typeof obj>(
  key: K, 
  params: Parameters<typeof obj[K]>[0]
) {
    const func: { [P in keyof typeof obj]:
        (arg: Parameters<typeof obj[P]>[0]) => void }[K] = obj[key]
    return func(params); // okay
}

By restructuring the types and mapping functions to their parameters, you can enhance type safety and ensure proper function calls without errors. This approach, while requiring some refactoring, ultimately improves the robustness and reliability of your TypeScript code.

Choose the refactoring approach that best suits your needs, considering factors like code readability and maintainability. Remember, the goal is to make your TypeScript code clear, concise, and error-free.

For further experimentation and learning, you can use this 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

Encountering an issue while attempting to incorporate an interface within a class in TypeScript

Can someone please help me figure out what I'm doing wrong? I'm attempting to implement an interface inside a class and initialize it, but I keep encountering this error: Uncaught TypeError: Cannot set property 'name' of undefined at n ...

Developing advanced generic functions in Typescript

I am currently working on a Hash Table implementation in Typescript with two separate functions, one to retrieve all keys and another to retrieve all values. Here is the code snippet I have so far: public values() { let values = new Array<T>() ...

Unusual Behavior of Observable.concat() in Angular 4 with RxJS 5

My Angular 4 / TypeScript 2.3 service has a method called build() that throws an error if a certain property is not initialized. I am attempting to create a safer alternative called safeBuild() that will return an Observable and wait for the property to be ...

Determine if the "type" field is optional or mandatory for the specified input fields in Typescript

I need to determine whether the fields of a typescript type or interface are optional or required. export type Recommendation = { id?: string, name: string, type?: string, tt: string, isin?: string, issuer: string, quantity?: nu ...

Is it feasible to evaluate a Typescript method parameter decorator at request time in a nodejs+nestjs environment rather than just at build time?

Looking to simplify my handling of mongodb calls with and without transactions in a single service method by writing a decorator. This would help eliminate the repetition of code and make things more efficient. Key points for usage: • Service class has ...

The data type returned by a method is determined by the optional parameter specified in the

I have a situation where I need to create a class with a constructor: class Sample<T> { constructor(private item: T, private list?: T[]) {} } and now I want to add a method called some that should return: Promise<T> if the parameter list ...

Attempting to clear the value of a state property using the delete method is proving to be ineffective

Within my React-component, there exists an optional property. Depending on whether this property is set or not, a modal dialog is displayed. Therefore, when the modal should be closed/hidden, the property must not be set. My state (in simplified form): i ...

Utilizing 'nestjs/jwt' for generating tokens with a unique secret tailored to each individual user

I'm currently in the process of generating a user token based on the user's secret during login. However, instead of utilizing a secret from the environment variables, my goal is to use a secret that is associated with a user object stored within ...

Implementation of a function in Typescript that can be defined with a

I am currently diving into the Typescript specification and I'm facing a challenge in creating a functional implementation for describable functions. https://www.typescriptlang.org/docs/handbook/2/functions.html The provided example lacks completene ...

Is there a way to customize the default MuiCheckbox icon in theme.ts?

How can I customize the icon default prop for Mui checkbox? I followed the instructions provided here and used a snippet from the documentation: const BpIcon = styled('span')(({ theme }) => ({ borderRadius: 3, width: 16, height: 16, .. ...

Definition file in TypeScript for an npm package provided by an external source - constructor

In my Node project, I am utilizing ES6 and Typescript. Despite this, there is a commonjs library that I need to incorporate. To address this, I have created my own .d.ts declaration file for the library: module "@alpacahq/alpaca-trade-api" { e ...

What is the best way to eliminate a particular element from an array produced using the .map() function in

I am experiencing an issue with my EventCell.tsx component. When a user clicks on the component, an event is created by adding an element to the components state. Subsequently, a list of Event.tsx components is rendered using the .map() method. The problem ...

Every time I click on a single button, all the text inputs get updated simultaneously

One issue I encountered is with a component featuring increment and decrement buttons. These buttons are meant to interact with specific products, yet when clicked, all text inputs update simultaneously instead of just the intended one. COMPONENT HTML: &l ...

Navigating back to the login page in your Ionic V2 application can be achieved by utilizing the `this.nav

Running into an issue with navigating back to the login screen using Ionic V2. Started with the V2 tabs template but added a custom login page, setting rootPage = LoginPage; in app.components.ts. When the login promise is successful, I used this.nav.setR ...

Arranging Alphanumeric Characters in Angular in Ascending Order

I am trying to sort a list of characters first, followed by alphanumeric values. Here is what I have: [Austria , Germany , 123aed , 234eds] This is my current sorting attempt: obj.sort((a,b) => { if ( (isNaN(a.text) && isNaN(b.text)) || ...

Jest | Testing Tool for Functions with Multiple Parameters

I have developed a new tool that requires 3 parameters: value (string), service (any - an actual GET service), and name (string). Below is the code for the tool: import { serverErrorResponseUtil } from 'util/serverErrorResponseUtil'; import { H ...

What is the most effective method to create a versatile function in Angular that can modify the values of numerous ngModel bindings simultaneously?

After working with Angular for a few weeks, I came across a problem that has me stumped. On a page, I have a list of about 100 button inputs, each representing a different value in my database. I've linked these inputs to models as shown in this snipp ...

Understanding the appropriate roles and attributes in HTML for a modal backdrop element in a TypeScript React project

Trying to create a Modal component using React and TypeScript with a feature that allows closing by clicking outside. This is being achieved by adding a backdrop element, a greyed out HTML div with an onClick event calling the onClose method. Encountering ...

Tips for preserving updates following modifications in Angular 5/6?

I'm currently working on enhancing the account information page by implementing a feature that allows users to edit and save their details, such as their name. However, I am encountering an issue where the old name persists after making changes. Below ...

Tips for extracting certain keys from an interface using template string types?

I am dealing with a code snippet here that accepts a string array named possibleColumns for a specific database table. It then takes the incoming source and attempts to find a column containing {source}_id. For example, if the possibleColumns includes [&q ...