Exploring the inner components of an entity without the need for external tools

I am currently enhancing TypeScript usage in a project by implementing generics. The challenge I am facing involves dealing with a complex object retrieved from the backend, which consists of a class with numerous attributes, most of which are classes themselves. In our form, we have a common change handler that currently contains a few instances of any to make it functional, but my goal is to enhance it using generics.

This code snippet provides a simplified version of the structure in my application, showcasing the same issues I encounter:

class PhoneNumber {
  prefix: number;
  exchange: number;
  line: number;
}

class Address {
  address1: string;
  address2: string;
  city: string;
}

class User {
  phone: PhoneNumber;
  address: Address;
}

const user = new User();

function setValue<A extends keyof User, T extends User[A], K extends keyof T, V extends T[K]>(
  value: V,
  fieldType: new () => T,
  fieldName: A,
  fieldAttribute: K
) {

  if (!user[fieldName]) {
    user[fieldName] = new fieldType();
  }

  const field = user[fieldName];

  // error here.
  field[fieldAttribute] = value;
}

setValue(408, PhoneNumber, 'phone', 'prefix');

I've attempted various approaches, and the closest solution I have reached exhibits the use of sane values for the generics during the function call at the end in my IDE:

function setValue<"phone", PhoneNumber, "prefix", number>()
. However, I encounter a compilation error:

TS2536: Type 'K' cannot be used to index type 'User[A]'.

Am I approaching this problem incorrectly? If I'm unable to resolve this issue, I may resort to segregating the different types into separate handlers as an alternative.

Furthermore, there's a scenario where some of the fields are arrays of objects, further complicating the situation.

Answer №1

When using the setValue function with specific argument types, there is a scenario where a subclass B can be passed as the fieldType (which extends class A), and target a fieldAttribute of B that may not exist in A:

class PhoneNumber2 extends PhoneNumber {
  extension: number;
}

// While this code complies with setValue types,
// it may fail if `user` already contains a basic PhoneNumber instance
setValue(408, PhoneNumber2, "phone", "extension");

// In the above scenario:
user["phone"]["extension"] = 408;
// ...but user["phone"] could actually be a PhoneNumber,
// not necessarily a PhoneNumber2!

To address this issue, you can modify the implementation by replacing K extends keyof T with K extends keyof User[A] to ensure the fieldAttribute targets the base class instead:

function setValue2<A extends keyof User, T extends User[A], K extends keyof User[A], V extends T[K]>(
  value: V,
  fieldType: new () => T,
  fieldName: A,
  fieldAttribute: K
) {

  if (!user[fieldName]) {
    user[fieldName] = new fieldType();
  }

  const field = user[fieldName];

  field[fieldAttribute] = value; // Now works correctly
}

setValue2(408, PhoneNumber, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'extension'); // Produces an error due to incorrect type assignment

You can also simplify the function signature by removing the generic type T:

function setValue3<A extends keyof User, K extends keyof User[A], V extends User[A][K]>(
  value: V,
  fieldType: new () => User[A],
  fieldName: A,
  fieldAttribute: K
) {}

Playground Link

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

Utilize toggle functionality for page rotation with rxjs in Angular framework

Managing a project that involves a container holding multiple cards across different pages can be overwhelming. To address this, the screen automatically rotates to the next page after a set time interval or when the user presses the space bar. To enhance ...

Troubleshooting a TypeScript error when trying to access an imported service in Angular 2

I've been working on creating a form that allows users to input required details, which will trigger a server call and save the information. However, no matter what I try, I keep encountering the following error: ORIGINAL EXCEPTION: TypeError: this.po ...

Creating a data structure that filters out specific classes when returning an object

Consider the following scenario: class MyClass {} class MyOtherClass { something!: number; } type HasClasses = { foo: MyClass; bar: string; doo: MyClass; coo: {x: string;}; boo: MyOtherClass; }; type RemovedClasses = RemoveClassTypes& ...

Example TypeScript code: Use the following function in Angular 5 to calculate the total by summing up the subtotals. This function multiplies the price by the quantity

I have a table shown in the image. I am looking to create a function that calculates price* quantity = subtotal for each row, and then sum up all the subtotals to get the total amount with Total=Sum(Subtotal). https://i.stack.imgur.com/4JjfL.png This is ...

Issue with vue-class-component: encountering TS2339 error when trying to call a method within

My vuejs application is being built using vue-cli-service. After a successful build, I encountered TS2339 errors in my webstorm IDE: Test.vue: <template> <div>{{method()}}</div> </template> <script lang="ts"> impor ...

Detecting Typescript linting issues in VSCode using Yarn version 3.2.3

Every time I try to set up a new react or next app using the latest yarn v3.2.3, my VS Code keeps showing linting errors as seen in the screenshot below. The main error it displays is ts(2307), which says Cannot find module 'next' or its correspo ...

Inputting Dates Manually in the Angular Material Datepicker Field

The datepicker function works well unless I manually type in the date. When I input a date between 01.MM.YYYY and 12.MM.YYYY, the value switches to MM.DD.YYYY. However, if I input 16.09.2021 for example, it remains as DD.MM.YYYY. Is there a way to change ...

How can I make TypeScript properly export function names for closure-compiler?

Here is the TypeScript code I am working with: namespace CompanyName.HtmlTools.Cookie { export function eraseCookie(name:string, path:string) { createCookie(name, "", path, -1); } export function readCookie(name:string) { ...

Utilizing Angular's ngx-bootstrap date range picker for a customized date range filtering system

Currently, I am incorporating ngx-bootstrap's datepicker functionality and utilizing the date range picker. This feature allows users to choose a start and end date. After selecting these dates, my goal is to filter the array(content) based on whethe ...

Is it possible to close a tab while the chrome extension popup is still active?

I am currently developing a Chrome extension that reads the content of the current tab and performs a heavy task on the backend. If I were to close the tab while the process is ongoing, how can I allow the user to do so without waiting for the task to fi ...

Angular time-based polling with conditions

My current situation involves polling a rest API every 1 second to get a result: interval(1000) .pipe( startWith(0), switchMap(() => this.itemService.getItems(shopId)) ) .subscribe(response => { console.log(r ...

A different approach to showcasing components in SvelteKit based on the width of the screen

In my SvelteKit app, I'm using this code to retrieve the current window size and switch the displayed component when the width is small: <script> let size; </script> <svelte:window bind:innerwidth{size}/> <div> {#if size &g ...

Vue version 3 is encountering an issue with a module that does not have an exported member in the specified path of "../../node_modules/vue/dist/vue"

After I updated my npm packages, errors started popping up in some of the imports from the 'vue' module: TS2305: Module '"../../node_modules/vue/dist/vue"' has no exported member 'X' The X instances affected inclu ...

TypeScript excels in typechecking when using two individual assignments, but may encounter issues when attempting typechecking with tuple

I am quite puzzled by a discovery I made and I am seeking to understand why TypeScript is displaying this behavior and what the underlying reason may be. Here is the code snippet: class A { constructor(public name : String, public x = 0, public y = 0) ...

Executing the routing component prior to any other tasks

Having an issue where the ProductsService is fetching data from the server and storing it in an Array. The ProductsComponent serves as the parent component, while the ProductsListComponent and ProductListItemsComponent are its children components. The flow ...

Just change "this.array[0]..." in the TypeScript code

There is a problem, this.myList[0], this.myList[1], this.myList[2], this.myList[3], // mylist data is 0 ~ 18... this.myList[18] I attempted to solve it by doing the following: for (let i = 0; i < this.myList.length; i++) { this.myList.push( ...

The static method in TypeScript is unable to locate the name "interface"

Is it possible to use an interface from a static method? I'm encountering an issue and could really use some help. I've been experimenting with TypeScript, testing out an interface: interface HelloWorldTS { name : string; } Here&ap ...

Launching a web application on Vercel, the back-end has yet to be initialized

I am currently working on a Next.js/TypeScript web application project hosted on Vercel's free tier. To run my project, I use npm run dev in the app folder to start the front end and then navigate to the back-end folder in another terminal and run npm ...

Angular2 routing directs me to a different URL path

In my Angular2 web application, I have set up the following routes: '', '/app', '/login', '/signin', '/validate', '/error', '**': I've defined a route configuration in app.router.t ...

What could be causing the issue with my React Native app's release version not opening on a physical Android device?

I encountered an issue while trying to install the Release version of my application. In order to test both the debug and release versions on my physical and virtual devices, I utilized the following commands: ./gradlew assembleDebug, ./gradlew assembleRel ...