Ways to utilize a field from an interface as a type of index

interface Mapping {
  "alpha": (a: string) => void
  "beta": (b: number) => void
}

interface In<T extends keyof Mapping> {
  readonly type: T,
  method: Mapping[T]
}

const inHandlers: In<"alpha"> = {
  type: "alpha",
  method(prop /* :string */) {}
}

Does anyone have a solution to retrieve the typeof In["type"] as the index of Mapping? This would eliminate the need to repeat "alpha" twice.

I attempted it already and somehow prop becomes automatically typed as any

interface In {
  readonly type: keyof Mapping,
  method: Mapping[In["type"]]
}
const inHandlers: In<"alpha"> = {
  type: "alpha",
  method(prop /* :any*/) {}
}

Answer №1

If you have a pre-defined Mapping structure, you can easily work with it.

Start by utilizing mapped types to generate all possible data structures:

interface Mapping {
  "x": (a: string) => void
  "y": (b: number) => void
}


type Variants<Dictionary> = {
  [Prop in keyof Dictionary]: {
    name: Prop,
    fn: Dictionary[Prop]
  }
}

// type Result = {
//     x: {
//         name: "x";
//         fn: (a: string) => void;
//     };
//     y: {
//         name: "y";
//         fn: (b: number) => void;
//     };
// }
type Result = Variants<Mapping>

By creating a nested object with allowed state representations, we then need to consolidate these values into a union or a discriminated union type. Consider the following approach:

type Values<T> = T[keyof T]

interface Mapping {
  "x": (a: string) => void
  "y": (b: number) => void
}


type Variants<Dictionary> = {
  [Prop in keyof Dictionary]: {
    name: Prop,
    fn: Dictionary[Prop]
  }
}

// type Result = {
//     name: "x";
//     fn: (a: string) => void;
// } | {
//     name: "y";
//     fn: (b: number) => void;
// }

type Result = Values<Variants<Mapping>>

The use of the Values utility type aggregates all values without their corresponding keys into a union, which aligns with our goal. We can simplify and improve it further:


type Values<T> = T[keyof T]

interface Mapping {
  "x": (a: string) => void
  "y": (b: number) => void
}


type Variants<Dictionary> = Values<{
  [Prop in keyof Dictionary]: {
    name: Prop,
    fn: Dictionary[Prop]
  }
}>


type Handlers = Variants<Mapping>

const x: Handlers = {
  name: "x",
  fn(prop /* :string */) { }
}

const y: Handlers = {
  name: "y",
  fn(prop /* :number */) { }
}

Interactive Playground

No additional generic arguments are needed for this implementation. You can also check out a similar question on Stack Overflow here, and my related article here.

Answer №2

To specify the parameter type, you can implement an identity function:

TS Playground

function createMapping <T extends keyof Mapping>(mapping: In<T>): In<T> {
  return mapping;
}

const xMapping = createMapping({
  name: "x",
  fn (prop) {} // prop is string
}); // xMapping is In<"x">

const yMapping = createMapping({
  name: "y",
  fn (prop) {} // prop is number
}); // yMapping is In<"y">

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

Interpolating strings in a graphQL query

Exploring the world of Gatsby and its graphQL query system for asset retrieval is a fascinating journey. I have successfully implemented a component called Image that fetches and displays images. However, I am facing a challenge in customizing the name of ...

What is the reason behind create-next-app generating .tsx files instead of .js files?

Whenever I include with-tailwindcss at the end of the command line, it appears to cause an issue. Is there any solution for this? npx create-next-app -e with-tailwindcss project_name ...

What is the best way to mock an internal function within my route using sinon?

Currently, I have my own internal function defined in the greatRoute.ts file: //in greatRoute.ts async function _secretString(param: string): Promise<string> { ... } router .route('/foo/bar/:secret') .get( async (...) => { ...

Is your async function lacking a return statement?

After completing the development of a new method for a bug I encountered, I noticed something interesting. Despite the fact that there is a potential scenario where the function may not return anything, the compiler did not flag any errors. It got me think ...

Is it possible to specify the data type of form control values when using the Angular Reactive form builder?

Is it possible to use typed reactive forms with Angular form builder? I want to set the TValue on the form control to ensure we have the correct type. For example: public myForm= this.fb.group({ name: ['', [Validators.required, Validators.max ...

Execute a specialized function with imported modules and specified parameters

Within an npm project, I am looking to execute a custom function with arguments, or ideally provide it as a script in the package.json file like this: npm run custom-function "Hello, World". Currently, I have a file called src/myFunction.ts: import * as e ...

Confirm button title by verifying part of the label that contains a space

I'm facing an issue with clicking a button using the following code: await page.getByRole('button', { name: '3 Employees' }).click(); The problem is that the button's name fluctuates based on the number of employees, causing ...

Accessing JSON data from a database using Angular

Wondering if there is a way to effectively access and manipulate JSON data stored in a database using Angular. While researching online, I keep coming across methods for reading JSON files from the Asset Folder, which does not align with what I need. What ...

Using TypeScript to ensure the correct typing for the return type of AsyncThunk when utilizing the 'unwrapResult' method from Redux Toolkit

Struggling to determine the appropriate return type for an AsyncThunkAction in order to utilize it with the unwrapResult method from Redux Toolkit (refer to: Redux Tookit: Unwrapping Result Actions): Here is how the Async thunk is declared in the Slice: e ...

Observables do not provide any results when used in a pipe with an image src

I recently created a custom pipe for the image src in my application: It is applied to selectors like this: <img [src]="myobject?.URL | secure" /> Here's the code snippet for the pipe: import { Pipe, PipeTransform } from '@angular/c ...

Switching between various components based on conditions within the same route in Angular

My goal is to have 2 separate views, one for the homepage and another for authentication. I want to display the LoginComponent on the route '/' and the SignupComponent on the route '/signup' if the user is not logged in, otherwise rende ...

React did not allow the duplicate image to be uploaded again

I've implemented a piece of code allowing users to upload images to the react-easy-crop package. There's also an "x" button that enables them to remove the image and upload another one. However, I'm currently facing an issue where users are ...

Creating a module within a component in angular - step by step guide

I am interested in dynamically creating a component inside another component. This will allow me to pass my dynamic HTML template directly to the decorator like this: //code /** * @param template is the HTML template * @param container is @ViewChild(& ...

Is it possible to apply a style change to several components at once using a single toggle switch

I am looking to implement a day/night feature on my web app, triggered by a simple toggle click. While I can easily add this feature to a single component using the navigation menu, I am faced with the challenge of incorporating it into multiple component ...

The Google APIs sheet API is throwing an error message stating "Invalid grant: account not found"

I need to retrieve data from a spreadsheet using the Sheet API. After setting up a project in Google Cloud Platform and creating a service account, I granted the account permission to edit the spreadsheet. I then downloaded the credentials in JSON format. ...

Discover the utility of the useHistory() hook in TypeScript for Class Components

Hello there, I am currently attempting to implement the following code snippet in my TypeScript-based class component: this.history.push({ pathname: `/search-results`, search: `${job}$${location}` } ...

Utilizing a powerful combination of Angular 5, PrimeNG charts, Spring Boot, and JHipster

I am facing an issue with creating charts using PrimeNG. The main challenge I'm encountering is the conversion of data from a REST API in Angular 5 (TypeScript) and retrieving the list of measurements from the API. I have an endpoint that returns my m ...

Grunt Typescript is encountering difficulty locating the angular core module

Question Why is my Grunt Typescript compiler unable to locate the angular core? I suspect it's due to the paths, causing the compiler to not find the libraries in the node_modules directory. Error typescript/add.component.ts(1,25): error TS23 ...

Using Required and Partial with an Array of Generic Types

I'm currently working with the following types: interface Color { color: string } type DarkerColor<T> = T & Color & { darker: string } type ColorInfo<T> = DarkerColor<T> & { hue: number luminance: number opacity ...

Tips on extracting value from a pending promise in a mongoose model when using model.findOne()

I am facing an issue: I am unable to resolve a promise when needed. The queries are executed correctly with this code snippet. I am using NestJs for this project and need it to return a user object. Here is what I have tried so far: private async findUserB ...