Retrieve a particular key from an interface using a function parameter

Essentially, I have two interfaces:


interface config {
    lang: "en" | "de";
    test: number;
}

interface Accessor {
    key ( k: keyof config ): config[k];
}

I am looking to dynamically determine the type of any element I access from the config type using the Accessor object. Is this achievable?



Please note that while my code is more intricate and direct access to keys is not feasible, it ultimately simplifies to this scenario.

Answer №1

It is important to note that the key() method signature within your Accessor interface should return an indexed access type structured as Config[X], where X represents the type of the parameter k. Your current approach, using Config[k], is incorrect because k refers to the value of the parameter, not its type. To correct this, you can utilize the typeof type operator in the following manner:

interface Accessor {
    key(k: keyof Config): Config[typeof k];
}

After making this adjustment, the code compiles without any errors and functions properly. For instance:

declare const a: Accessor;
const langProp = a.key("lang");
// langProp: number | "en" | "de" 🤔
const testProp = a.key("test");
// testProp: number | "en" | "de" 🤔

However, this implementation does not achieve the intended behavior. The resulting type from the key() call signature encompasses all possible property types of Config, regardless of the input. This occurs because the type of k is simply keyof Config, equating to the union "lang" | "test". Consequently, Config[typeof k] becomes Config[keyof Config], which resolves to Config["lang"] | Config["test"], leading to "en" | "de" | number. Therefore, the call signature fails to differentiate between different keys passed into it.


To address this issue, you must modify key() to make it generic with respect to the type of k. Instead of restricting k to be of type keyof Config, it should accept a generic type K, constrained to keyof Config:

interface Accessor {
    key<K extends keyof Config>(k: K): Config[typeof k];
}

In this updated version, K serves as a generic type parameter, declared within angle brackets after the method name but before the opening parenthesis for the function parameters. With this change, references to K are permissible elsewhere in the call signature.

The revised implementation yields the desired outcome:

declare const a: Accessor;
const langProp = a.key("lang");
// langProp: "en" | "de" 👍
const testProp = a.key("test");
// testProp: number 👍

It's worth noting that when a type already has a designated name, the typeof type operator is redundant. In our scenario, since k is of type K, typeof k can be simplified to just K:

interface Accessor {
    key<K extends keyof Config>(k: K): Config[K];
}

This streamlined version remains functional and achieves the expected results.

Access TypeScript Playground here

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

TS2559: Type 'String' lacks any common properties with type 'object'

When working with TypeScript, I encountered an issue with a map defined as shown below that is intended to accept (key, value) pairs of (string, anything). let map = new Map<String, Object>() Upon attempting to insert a (key, value) pair into the ma ...

Utilizing TypeScript with Context API

This is my first time working with Typescript to create a context, and I'm struggling to get it functioning properly. Whenever I try to define interfaces and include them in my value prop, I encounter errors that I can't figure out on my own. Spe ...

Similar to Java method references, TypeScript also provides a way to reference functions

Although I am familiar with Java, TypeScript is fairly new to me. In Java, lambda expressions (->) or method references (::) are commonly used to satisfy functional interfaces. It seems like lambda expressions function similarly in both languages (plea ...

Is there a way to identify the items that have been removed and added in an array by comparing an old array to a new array

Consider the following scenario: oldArray = [ { id: 1, position: 'DEV OM'}, { id: 2, position: 'Senior Developer'}, ] newArray = [ { id: 2, position: 'Senior Developer'}, { id: 3, position: 'Junior Devel ...

What is the process for developing an interface adapter using TypeScript?

I need to update the client JSON with my own JSON data Client JSON: interface Cols { displayName: string; } { cols:[ { displayName: 'abc'; } ] } My JSON: interface Cols { label: string; } { cols:[ { label:&a ...

Converting HTML to an array using Angular

Is there a way to convert HTML into an array of entities? For example: 'hi <em>there</em>' => ['hi', '<em>', 'there', '</em>'] ...

The error message "@graphql-eslint/eslint-plugin: problem with the "parserOptions.schema" configuration"

Our team is currently working on developing micro-services using NestJS with Typescript. Each of these services exposes a GraphQL schema, and to combine them into a single graph, we are utilizing a federation service built with NestJS as well. I recently ...

Is there a way to make a model behave like an object in loopback when typing?

One of the challenges I face is dealing with a loopback model that is often represented in raw JSON format. For example: @model() class SomeModel extends Entity { @property({ type: 'string' }) id?: string; } In its raw JSON form, it would l ...

Filling a data entry with simultaneous commitments

Sample code: type Alphabet = 'a' | 'b' | 'c'; const alphabetMap: Record<Alphabet, null> = { 'a': null, 'b': null, 'c': null} // Select any asynchronous processing function you prefer funct ...

Apply and remove the CSS style during the initialization and destruction phases

After creating a component for file preview of audio and video, I decided to use the material dialog. However, I encountered an issue regarding the transparency of the dialog background. In my SCSS component code, I attempted to set the background color o ...

Error message: Unable to locate module when utilizing my alternative library packaged with Rollup

Currently, I am utilizing rollup to package a UI library for use across various primary applications. However, the bundled ESM file contains imports that are incompatible with webpack in the main applications: import { ArrowDropDownCircleOutlined } from &a ...

The scale line on the OpenLayers map displays the same metrics twice, even when the zoom level is different

When using the Openlayers Map scale line in Metric units, a specific zoom rate may be repeated twice during the zoom event, even though the actual zoom-in resolution varies on the map. In the provided link, you can observe that the zoom rates of 5km and ...

Crafting a recursive Typescript Immutable.js Record through typing

I am currently working on representing a tree-like data structure using immutable js and typescript. At the moment, I am utilizing regular vanilla js objects to depict the nodes within the tree. Below is the type signature. type NodeType = { value: str ...

The category 'string[]' cannot be assigned to category 'never[]'

When working with typescript, I encountered this error message: Type 'string[]' is not assignable to type 'never[]'. I am trying to insert 'abc' into the array. Can you help me understand what I am doing wrong? export defau ...

Tips for detecting changes in @Input values

There is a component that has the following input: @Input() list: Array<string> I want to know how I can detect when the parent component changes the value of this input. ...

The issue with routerLink not functioning properly in e2e testing with Protractor within an Angular 2 environment

I have implemented an angular2 routerLink that allows users to navigate to the editComponent by passing the userId as a parameter. <a [routerLink]="['/edit',user._id]" (click)="returnUser(user._id)" id="redirect" class="btn" name="user-{{ ...

Exporting enum in TypeScript declaration file (`.d.ts`) results in a "Unable to resolve" error

yesterday I was updating an enum in our app.d.ts file, which contains most of the app-specific types. The contents of the file are as follows: // app.d.ts export module App { // … more definitions enum MyEnum { A, B, ...

Creating a cutting-edge object using Angular 4 class - The power of dynamism

Attempting to create a dynamic object within a function, but encountering recognition issues. function1(object: object) { return new object(); } The function is invoked as follows: function1(Test) 'Test' represents a basic Class implementatio ...

Accessing Angular's Observable Objects

I am currently in the process of learning Angular and trying to work with Observables. However, I am facing an issue where I cannot extract data from an Observable when it is in object form. public rowData$!: Observable<any[]>; UpdateGrid() { this ...

Encountering an issue: the argument with type '{ username: string; password: string; }' cannot be matched with the parameter of type 'RequiredEntityData<User>'

I have encountered an issue in my project where I keep getting a red underline at a specific line of code: { username: options.username, password: hasedPassword } Upon hovering over it, the error message reads as follows: Argument of type '{ ...