Selecting nested attributes from a class or interface: Best practices

I am looking to retrieve a specific type from the fn function. Let's take a look at the code snippet below:

for more information, this is a continuation of a previous question on Stack Overflow: this question

class Person {
  firstName: string;
  lastName: string;
}

class Book {
  title: string;
  author: Person;
}

type MakePropTypesAny<T> = T extends object
  ? {
      [K in keyof T]: MakePropTypesAny<Partial<T[K]>>;
    }
  : any;

type ClassConstructor<T> = new (...args: any[]) => T;

type ParseReturnType<T> = T; // < - - - - - - - - - - - - - - - - - - what here?

const fn = <T>(
  obj: ClassConstructor<T>,
  fields: Partial<MakePropTypesAny<T>>
): ParseReturnType<T> => {
  return {} as any;
};

const res = fn(Book, { author: { firstName: true } });

res currently has the type of Book, but I want it to have the following type instead:

type ToBeReturned = {
  author: {
    firstName: string; // because Person.firstName is 'string'
  };
};

The above type represents the fields passed in the fields argument, with field types taken from the Book class

Answer №1

To define the ParseReturnType, you follow a similar pattern to how the MakePropTypesAny was defined, but this time with input parameters fields and obj. By utilizing recursive conditional types, you iterate through the object structure of fields and simultaneously apply the keys of fields to obj (learn more). You can also experiment with this concept in the TypeScript playground):

class Person {
  constructor(public firstName: string, public lastName: string){}
}

class Book {
  constructor(public title: string, public author: Person){}
}

type MakePropTypesAny<T> = T extends object
  ? {
      [K in keyof T]?: MakePropTypesAny<T[K]>;
    }
  : true;

type ClassConstructor<T> = new (...args: any[]) => T;

// Iteratively examine U and T until reaching a basic type.
// Then return the current recursion depth's type of T.
type ParseReturnType<T, U> = U extends object ? {
  [K in keyof U]: ParseReturnType<T[Extract<K, keyof T>], U[K]>
}: T; 

function fn<T, U extends MakePropTypesAny<T>>(
  obj: ClassConstructor<T>,
  fields: U
): ParseReturnType<T, U> {
  return {} as any;
};

const res = fn(Book, { author: { firstName: true } } );
res.author.firstName // string
res.author.lastName // Property 'lastName' does not exist

const res2 = fn(Book, { author: { lastName: true, firstName: true} } );
res2.author.firstName // string
res2.author.lastName // string

In implementing ParseReturnType, using T[Extract<K, keyof T>] instead of

T[K]</code is crucial due to TypeScript's limitation where it might not recognize that <code>keyof U
lies within keyof T. By incorporating the utility type Extract, which acts as
Extract<T, U> = T extends U ? T : never
, we bridge this gap, ensuring the compiler understands the relationship between these key values.

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

What is the best way to refresh NGRX data?

There are two models in a one-to-many relationship: export interface GroupModel { id: number; name: string; userIds?: number[]; } export interface UserModel { id: number; name: string; groupId?: number; } An issue arises when updating either m ...

Using RXJS with the 'never' subject as the specified generic type

In my current Angular project, I am using RXJS and Typescript. The code snippet below shows what I have implemented: const sub = new Subject<never>(); My understanding is that the above line implies that any subscriber defining the 'next' ...

Coloring intersected meshes in three.js will recolor every mesh in the scene

My attempt to change the color of a mesh on mouse hover is not functioning as expected. Instead of coloring only one mesh red, every single mesh is being filled with the color. Upon inspecting the intersected objects during debugging, it shows only one el ...

Is it okay for a function to be 'awaited' in TypeScript without causing an error?

Waiting for a function that is not even called is a futile endeavor. Surprisingly, neither a compile time error nor a runtime error is generated in such cases. The perplexing question remains - in what scenarios would one wait for a function to be execut ...

Preparing a component for evaluation

When I execute the app and load views using @useview('resources/panels/data-table-panel.html'), everything works fine. However, running a component test results in failure due to a 404 error caused by the html file not being found. After changin ...

Sending a parameter to a route guard

I've been developing an application that involves multiple roles, each requiring its own guard to restrict access to various parts of the app. While I know it's possible to create separate guard classes for each role, I'm hoping to find a mo ...

When a const variable is declared within the composition-api setup(), it remains unchanged unless redeclared within the function scope

Being primarily a back-end developer, the front-end side of things is still relatively new to me. I'm aware that there are concepts in this domain that I haven't fully grasped yet, and despite my efforts, I'm unable to resolve a particular i ...

Implement dynamic changes to CSS variable values using Typescript during runtime

My angular 4 web application requires dynamic styling based on color values and image URLs received from the backend. While I have successfully set up the app to load images from the server, handling color values poses a greater challenge. The primary colo ...

Validation issue with Reactive Forms not functioning as expected

My latest project involves a user signup component that I created from scratch import { Component } from '@angular/core'; import {UserManagementService} from '../user-management.service'; import {User} from "../user"; import {FormBuild ...

Playing around with Jest (Typescript - Simulate chrome namespace)

I've developed a chrome extension using React. I've started adding some tests, but I'm struggling with a few of them. The issue lies in testing my <App/> component, as I keep encountering errors no matter what approach I take. Here&a ...

How can I prevent v-btn from triggering a route when clicked, but still display the active state based on the route?

My toolbar menu is looking great, with v-btn as the activator. However, I am facing an issue on mobile devices where hover doesn't work. Whenever I click on the button, it triggers the route instead of just opening the menu. I have set :on route to au ...

Prevent the click event on an Angular component

I am currently utilizing the component found on this link: I have not been able to locate a function or property that disables the component state, making it impossible to select dates on the calendar. Due to this limitation, I am interested in learning ...

Executes the function in the child component only if the specified condition evaluates to true

When a specific variable is true, I need to call a function in a child component. If the variable is false, nothing should happen. allowDeleteItem = false; <ChildComponent .... removeItemFn={ deleteFn } /> I attempted to use the boolean variable wi ...

Effortlessly apply mapping, filtering, reducing, and more in JavaScript

Array#map and Array#filter both create a new array, effectively iterating over the original array each time. In languages like rust, python, java, c#, etc., such expression chains only iterate once, making them more efficient in certain cases. While this ...

How to Link an Observable Array to Another in Angular 2

In my Angular 2 project, I am trying to chain multiple HTTP calls together. Initially, I retrieve an array of environments with one call. For each environment, I want to make another call and create objects like firstObject[{name:"name",secondObject[stuff ...

Leveraging the expand function for pagination through recursive invocations

I am currently working on retrieving data from a third party API that necessitates manual management of paging by keeping track of the number of records retrieved versus the total number of records available. In attempting to handle this, I experimented w ...

The MatInput value will only display after the page is reloaded or refreshed

After refreshing the page, a matInput field displays a value or result that was previously hidden. https://i.stack.imgur.com/q9LQI.png By selecting or highlighting within the matInput, the value or result becomes visible. https://i.stack.imgur.com/SqaLA.p ...

What is the relationship between Typescript references, builds, and Docker?

I am facing a dilemma with my projectA which utilizes a common package that is also needed by my other Nodejs services. I am unsure of the best approach to package this in a Docker file. Ideally, running tsc build would compile both the project and the dep ...

Is the Cyrillic encoding feature not functioning properly in Angular 4 with .Net Core 2?

Struggling with handling Cyrillic characters in my current project. Utilizing .Net Core 2 along with Angular 4.2.5 I've noticed that displaying a string in the templates using {{ someCyrillicString }} works fine. However, encountering issues when tryi ...

Reorganizing Execution Order in AfterViewInit with Subscriptions in Angular 10

In my component, I am using the ngAfterViewInit lifecycle hook to handle certain tasks: ngAfterViewInit() { this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); this.subscription = this.dataService.dataChanged .subscribe( ...