Utilizing Typescript to ensure property keys within a class are valid

Looking for advice to make a method more generic. Trying to pass Child class property keys as arguments to the Super.method and have Child[key] be of a Sub class.

class Parent {
  method<T extends keyof this>(keys: T[]){
  }
}

class Child extends Parent {
  a = new Sub;
  b = new Sub;
  c = new Sub;
  d = new Nope;
  e = new Nope;
}

child = new Child;
child.method(['a', 'b', 'c']);

All property keys are currently being displayed. However, unsure about filtering the classes next.

The assistant has displayed the keys as follows.

  • 'a'
  • 'b'
  • 'c'
  • 'd'
  • 'e'

Desire a change as shown below:

  • 'a'
  • 'b'
  • 'c'

Answer №1

Indeed, it is possible to make this happen automatically.

However, a specification for the Sub or Nope types was not provided, so I will define them. This distinction is crucial due to TypeScript's structural typing nature; it focuses on structure rather than names. If both Sub and Nope share the same properties, they will be considered the same type by the compiler, making it impossible to differentiate between them based on their class properties. Therefore, let's define them as follows:

// It's important that Sub and Nope are structurally distinct types
// for proper differentiation by the compiler
class Sub {
  sub!: string;
}
class Nope {
  nope!: string;
}

Now, Sub has a key named "sub", while Nope has a key named "nope", allowing the compiler to distinguish between them.

You can introduce a type alias called KeysMatching<T, V>, which retrieves keys from T where the property matches type V:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];

This implementation utilizes mapped and conditional types. You can now properly type your method by replacing keyof this with KeysMatching<this, Sub>:

class Parent {
  method<T extends KeysMatching<this, Sub>>(keys: T[]){
  }
}

class Child extends Parent {
  a = new Sub;
  b = new Sub;
  c = new Sub;
  d = new Nope;
  e = new Nope;
}

Let's confirm its effectiveness:

const child = new Child;
child.method(['a', 'b', 'c']); // okay
child.method(['d','e']); // error!

It appears to be working correctly. You can test it further using this Playground link. Hopefully, this explanation helps you achieve your objectives. Best of luck!

Answer №2

To prevent d and e from being valid selections, you can either remove them from your class or set them as private. This will limit the auto-completion list to only display 'a', 'b', 'c', 'method' - with 'method' included due to inheritance from the base class.

If you want even more control, you could choose to restrict the names further, but this approach may require constant maintenance:

class Parent {
    protected baseMethod<T extends keyof this>(keys: T[]){
    }
}

class Child extends Parent {
    a = new Sub();
    b = new Sub();
    c = new Sub();
    d = new Nope();
    e = new Nope();

    method<T extends keyof this & 'a' | 'b' | 'c'>(keys: T[]) {
        this.baseMethod(keys);
    }
}

In the scenario above, trying to include d or e will trigger a warning message.

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

Cannot send response headers once they have already been sent to the client [NEXTJS]

Currently, I am engrossed in a personal project focused on creating a dashboard using NextJS. This project serves as an opportunity for me to delve into learning NextJS and the fundamental concepts of TypeScript. My primary challenge at the moment revolves ...

The module '@angular/core' could not be located in the '@angular/platform-browser' and '@angular/platform-browser-dynamic' directories

Attempting to incorporate Angular 2.0.0 with JSMP, SystemJS, and TS Loader in an ASP.NET MVC 5 (non-core) application. Encountering errors in Visual Studio related to locating angular modules. Severity Code Description Project File Line Suppr ...

What is causing the issue where search query parameters are not recognizing the initially selected option?

Hey, I'm having an issue with searchParams. The problem is that when I apply filters like "Breakfast, Lunch, Dinner", the first chosen option isn't showing up in the URL bar. For example, if I choose breakfast nothing happens, but if I choose lun ...

Unlocking 'this' Within a Promise

I seem to have an issue with the 'this' reference in the typescript function provided. It is not correctly resolving to the instance of EmailValidator as expected. How can I fix this so that it points to the correct instance of EmailVaildator, al ...

Issue encountered with Ionic and ssh2: process.binding is not supported

I am currently delving into the world of Ionic and experimenting with creating a basic application that utilizes SSH2 to establish an ssh connection between the app and a server. Here is a breakdown of the steps I took to encounter the issue: Steps to Rep ...

tsc is not recognizing the configurations in my tsconfig.json file

Running tsc in my project's directory is causing an error to be outputted (as shown below). This is my first attempt at using TypeScript and Node.js. Please consider me as a complete beginner. Operating system: Ubuntu 15.10 64bits NPM version: 2.4. ...

NodeJS can be used to convert JSON data into an XLSX file format and allow for

I am currently working on a project in nodejs where I need to convert JSON data into XLSX format and then download it to the client's browser. I have been using the XLSX npm module to successfully convert the JSON data into a Workbook, however, I am f ...

Reimagine server-side storage options as an alternative to remixing JavaScript local storage

My remix application is designed to serve as a frontend. I retrieve data from the backend and sometimes need to load specific data only once and reuse it across multiple pages. In our previous frontend setup, we utilized localstorage; however, with the cur ...

TS: A shared function for objects sharing a consistent structure but with varied potential values for a specific property

In our application, we have implemented an app that consists of multiple resources such as Product, Cart, and Whatever. Each resource allows users to create activities through endpoints that have the same structure regardless of the specific resource being ...

There seems to be an issue with the React Native FlatList: It appears that there is no overload matching this call and some

I am currently learning React Native and attempting to create a basic chat room application. I am facing an issue with the FlatList component that I can't seem to resolve. Even though I have provided both the data prop and renderItem prop to the FlatL ...

What is the method for typing an array of objects retrieved from realmDB?

Issue: Argument type 'Results<Courses[] & Object>' cannot be assigned to the parameter type 'SetStateAction<Courses[]>'. Type 'Results<Courses[] & Object>' lacks properties such as pop, push, reverse, ...

What are the steps to utilize a personalized validation access form within a component?

I created a unique validator to verify if an email already exists in the database before saving a new user, however, it appears that the validator is not functioning properly. Here is my template: <form class="forms-sample" #f="ngForm" (ngSubmit)="onS ...

What is the proper way to declare and utilize a constant list within a component template in NuxtJs?

Can someone help me with using itemList in a template? The itemlist is a static list, but I am unsure of where to declare it and how to export it to the template. <template> <table class="table table is-striped is-narrow is-fullwidth" ...

Encountering issues with integrating interactjs 1.7.2 into Angular 8 renderings

Currently facing challenges with importing interactive.js 1.7.2 in Angular 8. I attempted the following installation: npm install interactjs@next I tried various ways to import it, but none seemed to work: import * as interact from 'interactjs'; ...

Angular 5 error: Decorators do not support function expressions

I am struggling to compile my angular project using the command ng build --prod The issue arises in the IndexComponent : index.componenent.ts import { Component, OnInit } from '@angular/core'; import { indexTransition } from './index.anim ...

Analyzing a string using an alternative character

I want to convert the string "451:45" into a proper number. The desired output is 451.45. Any help would be appreciated! ...

Incoming information obtained via Websocket

Currently, I am working with Angular and attempting to retrieve data from the server using websockets. Despite successfully receiving the data from the server, I am faced with a challenge where instead of waiting for the server to send the data, it retur ...

Number as the Key in Typescript Record<number, string> is allowed

As a newcomer to TypeScript, I am still learning a lot and came across this code snippet that involves the Record utility types, which is quite perplexing for me. The code functions correctly in the playground environment. const data = async (name: string ...

Navigating Angular single page application routes within an ASP.NET Web API project

Developed an Angular single-page application with specific routes: /messages /messages/:id By using ng serve, I can navigate to these pages at URLs like localhost:4200/messages and localhost:4200/messages/:id After building the solution, I transferred t ...

In TypeScript, is it possible to indicate that a function will explicitly define a variable?

In TypeScript, I am working on creating a class that delays the computation of its information until it is first requested, and then caches it for future use. The basic logic can be summarized as follows. let foo: string | undefined = undefined; function d ...