Guide to declaring a dynamic type variable based on a constructor parameter

Consider the following class structure:

class DataExtractor<T, K extends keyof T> {
    constructor(private data: T, private property: K) { }

    extractData(): T[K] {
        return this.data[this.property];
    }
}

This approach may seem awkward to work with or extend because the type parameter K is limited to a string literal type.

I am interested in defining the type parameters as <T, R> where R represents the equivalent of T[K]. Unfortunately, TypeScript restricts the ability for R to reference a parameter passed to the constructor, and the syntax for the property parameter is not designed to refer to the parameter itself. Additionally, it is not possible for the constructor to possess generic type parameters.

Given these constraints, where should I indicate that R = T[typeof property]?

Answer №1

I'm not entirely certain about your intentions, so I can't determine the most suitable solution for your situation:


One approach involves utilizing conditional types to derive K from types T and your desired R. However, this method may introduce more complexities than it resolves:

type ValueOf<T> = T[keyof T];
type KeyofMatching<T, R extends ValueOf<T>> = 
  ValueOf<{ [K in keyof T]: T[K] extends R ? K : never }>;

The KeyofMatching type alias takes a type T and one of its property value types R, returning all the keys K associated with that type. Hence, T[KeyofMatching<T, R>] always equals R. Unfortunately, TypeScript may require an assertion when converting values between these types. A function can facilitate this conversion:

function asR<T, R extends ValueOf<T>>(x: T[KeyofMatching<T, R>]): R {
  return x as any;
}

By defining a class as shown below, you can extract data based on the given parameters:

class Extractor<T, R extends T[keyof T]> {
  constructor(private item: T, private key: KeyofMatching<T, R>) { }
  extract(): R {
    return asR(this.item[this.key]); // note the asR()
  }
}

While functional, creating a new instance of Extractor may result in wider inferred types for R:

const extractorNotSoGood = new Extractor({a: "you", b: true}, "b");
// inferred as Extractor<{a: string, b: boolean}, string | boolean>;

To ensure narrower type inference for R, explicit specification might be necessary:

const e = new Extractor<{a: string, b: boolean}, boolean>({a: "you", b: true}, "b");

An alternative approach involves using a static builder method instead of the constructor, along with storing a function instead of just a key:

class Extractor<T, R extends T[keyof T]> {
  constructor(private item: T, private extractorFunction: (x: T) => R) { }
  extract(): R {
    return this.extractorFunction(this.item);
  }
  static make<T, K extends keyof T>(item: T, key: K): Extractor<T, T[K]> {
    return new Extractor(item, i => i[key]);
  }
}

Utilizing the make method provides better behavior and inference capabilities:

const e = Extractor.make({ a: "you", b: true }, "b");
// e is an Extractor<{a: string, b: boolean}, boolean>

This method presents its own challenges but could offer a viable solution. Hopefully, one of these approaches helps you move forward. Best of luck!

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 with creating an App Runner on AWS CDK

Attempting to deploy my application using App Runner within AWS via CDK. Utilizing the reference from https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apprunner.Service.html. Upon deployment, encountering the following error: create_failed: R ...

How can I limit the keys in a Typescript object to only certain strings?

Is there a way in Typescript to create an object of type Partial with keys that can only be a combination of 'a', 'b', or 'c'? The object should not have all 3 keys, but it must have at least one. Here's what I've at ...

Adal TypeScript Document

Recently, I've been experimenting with the TypeScript version of adal.js. As part of my setup process, I'm referring to this link to install adal.ts. However, after executing the command: npm install adal-typescript --save a new "node_modules" ...

It seems that the Angular2-google-maps library is having trouble with installation

Attempting to install the angular2-google-maps library is resulting in errors. The desired library can be found at: The specific error encountered is displayed here: https://i.stack.imgur.com/L2vOY.png Any assistance with this issue would be greatly app ...

Mastering the utilization of API routes within the Next JS 13 App Router framework

As a newcomer to React JS and Next.js, I recently made the switch from using the Page Router API in Next.js to utilizing the new App Router introduced in Next.js 13. Previously, with the Page Router, creating a single GET request involved nesting your "JS ...

Ng2-NoUISlider displaying consistent range values

I'm facing an issue with implementing multiple sliders on a single page where each slider should have different range values. Here is a snippet of my HTML: <div *ngFor="let property of comepleteFilters"> <h5>{{property.pro ...

What steps can be taken to prevent typescript from converting unicode characters to ascii?

Consider the following scenario: const example = ts.createSourceFile('test.ts', 'console.log(" ...

Error: Vue Prop is undefined and cannot be utilized within a v-for loop when using TypeScript and decorators

Hey there, I'm currently utilizing Vue along with typescript and facing an issue with props in v-for where it's not rendering anything. Check out the code snippet below for reference I've experimented with computed props, setting default va ...

What method can be used to fetch generic type parameter in typescript?

I am having trouble finding the type of E within the code provided below. class A<E> { getParameterType() { // I need to determine the type of E } } class B { } ** Example ** new A<number>().getParameterType() // number new A<B&g ...

The Typescript interface requires that one prop representing a number is expected to be less than another number prop

Is there a way to create a TypeScript interface that enforces a condition where a number property must always be less than another number property in the interface? For instance, if I have a numberOfFiles property and a currentFileIndex property, and I wa ...

In the realm of Angular and TypeScript, one may encounter an error stating that '"void' cannot be assigned to a parameter of type '(value: Object) => void"

I’m facing difficulties in resolving this issue. I created a web API using .NET, and now I’m attempting to call this API in an Angular project. //movie.ts export class Movie{ ID: number; Title: number; GenreId: number; Duration: number ...

Error encountered: Parsing error in Typescript eslint - The use of the keyword 'import' is restricted

My CDK application is written in typescript. Running npm run eslint locally shows no errors. However, when the same command is executed in a GitLab pipeline, I encounter the following error: 1:1 error Parsing error: The keyword 'import' is r ...

I'm currently endeavoring to integrate SignalR into my Vue project and encountering an issue

Currently, I am working on a project using Vue with TypeScript and I am interested in integrating SignalR. I have thoroughly studied the documentation provided by '@dreamonkey/vue-signalr' on how to utilize SignalR. import { VueSignalR } from &ap ...

"Error: Module not found" or "The import path cannot have a tsx extension" appears while setting up Storybook with TypeScript

Currently, I am working on a component that requires the use of another component. To import the necessary component, I added the line: import { Text } from "../Text/Text" into the file /src/stories/TextArea/TextArea.tsx. Unfortunately, this r ...

Creating dynamic dxi-column with different data types in dxDataGrid

Our team is currently working on an angular application that involves displaying records in a dxdatagrid. The challenge we are facing includes: Different schema each time, with data coming from various tables. The need to add/edit records. Displayi ...

Retrieve a specific subdirectory from the bundle

I've developed a Typescript package with the following file structure: package.json src/ index.ts common/ index.ts sub/ index.ts My goal is to import certain modules from the package like this: import {...} from '<package>&ap ...

Ways to eliminate additional data points on the x-axis in Highcharts

I'm currently using Highcharts to plot data for specific ID's, but I'm experiencing a display issue where extra data points are showing up on the x-axis. I only want to show certain data points on the x-axis and am not sure how to remove the ...

What is a superior option to converting to a promise?

Imagine I am creating a function like the one below: async function foo(axe: Axe): Promise<Sword> { // ... } This function is designed to be utilized in this manner: async function bar() { // acquire an axe somehow ... const sword = await foo ...

Creating TypeScript types for enums can make your code more robust and ensure that you are using

I need to create an interface based on the values of an enum for a React use-case. The enum contains key value pairs that are used as form IDs. When the value of an input element is changed in an event listener, I want to set the state using this.setState( ...

Ways to expand the DOM Node type to include additional attributes

I've been diving into typescript and transitioning my current code to use it. In the code snippet below, my goal is: Retrieve selection Get anchorNode from selection -> which is of type 'Node' If anchorNode has attributes, retrieve attr ...