Discover the accurate `keyof` for a nested map in TypeScript

Here is the code snippet I'm working on:

const functions={
    top1: {
        f1: () => 'string',
        f2: (b: boolean, n: number) => 1
    },
    top2: {
        f3: (b: boolean) => b
    }
}

I am looking to define an apply function as follows:

function apply (top: keyof typeof functions, functionName: string, inputs: any[]) {
    return functions[top][functionName](...inputs)
}

This will allow me to output different values using console.log:

console.log(apply('top1', 'f1', [])); // 'string'
console.log(apply('top1', 'f2', [true, 23])); // 1
console.log(apply('top2', 'f3', [false])); // false
apply('top2', 'f3', [1]); // should throw a TS error

However, when in strict mode (--strict), the following error occurs:

"Element implicitly has an 'any' type because type '...' has no index signature"

This issue arises since functionName is defined as a string, not as a keyof typeof functions[section]. How can I resolve this?

Answer №1

To ensure type safety and allow indexing, generic type parameters must be used to capture the concrete keys passed to the function.

Conditional types can also be utilized to extract parameter types and return types for enhanced type safety when handling arguments and return values.

const functions={
    top1: {
        f1: () => 'string',
        f2: (b: boolean, n: number) => 1
    },
    top2: {
        f3: (b: boolean) => b
    }
}

type WeakParameters<T> = T extends (...a: infer A) => any ? A : never;
type WeakReturnType<T> = T extends (...a: any) => infer R ? R : never; 
function apply<KOutter extends keyof (typeof functions),
    KInner extends keyof (typeof functions[KOutter])>(
        top: KOutter, functionName: KInner, inputs: WeakParameters<(typeof functions)[KOutter][KInner]>) : WeakReturnType<(typeof functions)[KOutter][KInner]>{
    var fn = functions[top][functionName]; // OK
    return (fn as unknown as (...a:any[])=> any)(...inputs)
}
console.log(apply('top1', 'f1', [])); // 'string'
console.log(apply('top1', 'f2', [true, 23])); // 1
console.log(apply('top2', 'f3', [false])); // false
apply('top2', 'f3', [1]); // show throw TS error

Note Despite being able to index into `functions` with `top` and `functionName`, a type assertion is still necessary as TS cannot infer that `fn` is a function. This limitation also prevents the use of built-in conditional types like `Parameters` and `ReturnType`, necessitating the creation of custom versions for extracting parameter and return types without requiring proof of being a function.

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

Integrating modules in Angular 2

Exploring the functionalities of Angularjs 2.0, I encountered an issue when attempting to inject a service into a class. Below is the code snippet that's causing trouble: import {Component, View, bootstrap, NgFor, HttpService, Promise} from 'ang ...

No results returned by Mongoose/MongoDB GeoJSON query

I have a Schema (Tour) which includes a GeoJSON Point type property called location. location: { type: { type: String, enum: ['Point'], required: true }, coordinates: { type: [Number], required: true ...

The Angular checked functionality is not working as expected due to the presence of ngModel

Just getting started with Angular here. I’m working on a checkbox table that compares to another table and automatically checks if it exists. The functionality is all good, but as soon as I add ngModel to save the changes, the initial check seems to be ...

Is it possible to optimize the performance of my React and TypeScript project with the help of webpack?

I am working on a massive project that takes 6 to 8 minutes to load when I run npm start. Is there a way to speed up the loading process by first displaying the sign-in page and then loading everything else? ...

Error: UserService (?) is missing parameters and cannot be resolved

Upon compiling my application, an error is appearing in the console: Uncaught Error: Can't resolve all parameters for UserService (?) Despite having @Injectable() present for the UserService, I am unsure where to troubleshoot further. import {Inj ...

Challenges in designing components in Angular 2.0 and beyond

Issue at hand - There are two input controls on the same page, each belonging to separate components. When a value is entered into the first input box, it calculates the square value and updates the second input control accordingly. Conversely, if the v ...

Creating an npm library using TypeScript model classes: A step-by-step guide

Currently, I am working on a large-scale web application that consists of multiple modules and repositories. Each module is being developed as an individual Angular project. These Angular projects have some shared UI components, services, and models which ...

I'm having trouble with Angular pipes in certain areas... but I must say, Stackblitz is truly incredible

Encountering this issue: ERROR in src\app\shopping-cart-summary\shopping-cart-summary.component.html(15,42): : Property '$' does not exist on type 'ShoppingCartSummaryComponent'. The error disappears when I remove the c ...

The TS2345 error is triggered when using the fs.readFile function with specified string and

Attempting to utilize the fs.readFile method in TypeScript, my code looks like this... import {readFile} from 'fs'; let str = await readFile('my.file', 'utf8'); This results in the following error message: TS2345: Argumen ...

What is the best way to handle various sections with changing structures within a complex form using react-hook-form?

I am working on a complex form that has sections A, B, and C, each of which can be in shape A1 or A2, B1 or B2, C1, or C2. Users are required to fill out settings based on whether the section is set to "advanced" or "basic". I want users to submit the enti ...

Tips on handling multiple Redux toolkit CreateApi interceptors

I came across this solution here for implementing a reAuth baseQuery in Redux Toolkit. I have several backend services that all use the same refresh token concept. Is there a way to create a single baseQueryAuth function that can be used by multiple creat ...

Using TypeScript, pass an image as a prop in a Styled Component

I am facing an issue with the code below that is supposed to display the "NoBillsLaptopPNG.src" image on the screen, but for some reason, the image is not showing up. The images are being imported correctly, so I'm unsure why the image is not appeari ...

Encountering a Circular JSON stringify error on Nest.js without a useful stack trace

My application is being plagued by this critical error in production: /usr/src/app/node_modules/@nestjs/common/services/console-logger.service.js:137 ? `${this.colorize('Object:', logLevel)}\n${JSON.stringify(message, (key, value ...

Using parameters and data type in Typescript

When I remove <IFirst extends {}, ISecond extends {}> from the declaration of this function, the compiler generates an error. Isn't the return value supposed to be the type after the double dot? What does <IFirst extends {}, ISecond extends { ...

What is the best way to export a default object containing imported types in TypeScript?

I am currently working on creating ambient type definitions for a JavaScript utility package (similar to Lodash). I want users to be able to import modules in the following ways: // For TypeScript or Babel import myutils from 'myutils' // myuti ...

TypeScript requires that the `includes` function must have the same type parameter for both input and

When working with TypeScript, I've encountered an interesting dilemma regarding the use of the Array.Prototype.includes function. It seems that this function requires me to pass in the same type as in the original array, but isn't the purpose of ...

Automatic generation of generic types in higher-order functions in TypeScript

function createGenerator<P extends object>(initialize: (params: P) => void) { return function (params: P): P { initialize(params) return params } } const gen = createGenerator(function exampleFunction<T>(param: T) { console.lo ...

What is the best way to choose checkboxes from data that is passed dynamically?

https://i.stack.imgur.com/L3k59.png I am looking to add an edit feature to my application. When the user clicks on the edit option, they should be taken to a different page with the previously entered value displayed. While I have successfully retrieved ...

Firebase Function deployment encountered an issue during the build phase, despite the predeploy process

My react.js project includes Firebase functions that are configured in a sub-folder called root/functions. These functions are written in typescript and have paths option set in tsconfig.json. In my functions/index.ts file, I import files from various loca ...

Issue arises when fastify/websocket is being used and an argument of type '{ websocket: boolean; }' is not compatible or able to be assigned to a parameter

I am facing an issue with my new project that involves fastify and Typescript. The error I am encountering is as follows: Argument of type '{ websocket: boolean; }' is not assignable to parameter of type 'RouteShorthandOptions ...ts(2345) B ...