The concept of class inheritance and the utilization of the "this" keyword in both static and public methods

I'm delving into the depths of class inheritance trying to unravel its mysteries. Although I have some techniques to handle static method typing, I'd like to delve into a common scenario I encounter and explore alternative perspectives.

Consider a Root class with essential methods that every subclass should possess. These methods include public methods (main and fork) along with a static method main, each accepting a single argument props which is a plain object. Each subclass enriches this object by adding specific properties relevant to that subclass.

While constructors are unnecessary for these classes, I recognize the potential utility of utilizing argument inheritance such as

ConstructorParameters<T>[0]
. I've experimented with T['props'] without success, and also considered leveraging InstanceType<T> for static methods using T extends Root with this: T defined. However, adapting this approach necessitates class-specific typing for props.

If required, I am open to incorporating a class generic but would prefer exploring alternatives before resorting to it.

class Root {
  props?: {root?: boolean} = {};
  main(props?: Root['props']) {
    this.props = {...this.props, ...props};
    return this;
  }
  fork(props?: Root['props']) {
    return new Root().main({...this.props, ...props});
  }
  static main(props?: Root['props']) {
    return new this().main(props);
  }
}

class Alpha extends Root {
  props?: Root['props'] & {alpha?: boolean};
}

class Beta extends Alpha {
  props?: Alpha['props'] & {beta?: boolean};
}

class Gamma extends Beta {
  props?: Beta['props'] & {gamma?: boolean};
}

const g = Gamma.main({beta: true}); // fails beta & returns instance of Root :(
console.log(g);

Playground

How can I properly type these methods so that g is identified as an instance of Gamma, with the props argument for Gamma.main encompassing gamma, beta, and alpha?

Answer №1

Here is a possible solution that involves the use of generics:

Experiment

type GetProps<T extends new (...args: any) => any> = ConstructorParameters<T>[0];

class Root<T extends typeof Root> {
  constructor(public props: {root?: boolean} = {}) {};
  main(props?: GetProps<T>) {
    this.props = {...this.props, ...props};
    return this;
  }
  fork(props?: GetProps<T>) {
    return new Root().main({...this.props, ...props});
  }
  static main<T extends typeof Root>(this: T, props?: GetProps<T>): InstanceType<T> {
    return new this().main(props) as InstanceType<T>;
  }
}

class Alpha<T extends typeof Alpha> extends Root<T> {
  constructor(public props: GetProps<typeof Root> & {alpha?: boolean}) {
    super(props);
  };
}

class Beta<T extends typeof Beta> extends Alpha<T> {
  constructor(public props: GetProps<typeof Alpha> & {beta?: boolean}) {
    super(props);
  };
}

class Gamma<T extends typeof Gamma> extends Beta<T> {
  constructor(public props: GetProps<typeof Beta> & {gamma?: boolean}) {
    super(props);
  };
}

const a = Gamma.main({beta: true})
const b = Gamma.main({beta: true}).main({beta: true})

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

Simplify an array in Javascript

I have a collection of objects structured in the following way: let list = [ { 'items': [ 'item 1', 'item 2' ] }, { 'items': [ 'item 3' ] } ] My goal is to flatte ...

What is the best method to generate an array of objects from a specific class and efficiently transfer them to subclasses?

At the outset, I want to emphasize that I am pursuing a degree in Computer Science and the academic institution I attend enforces strict guidelines against any form of plagiarism. Therefore, I kindly request responses that offer guidance and suggestions ra ...

How can I customize icons within the <mat-nav-list> using Angular?

Is it possible to change the icons as follows? If the list is expanded, display icon "arrow1" Otherwise, show icon named "arrow2". How can I determine if the list is expanded and implement this in HTML? <mat-nav-list> <mat-list-item> ...

Converting a string to an HTML object in Angular using Typescript and assigning it to a variable

I am facing an issue with passing HTML content from a service to a div element. I have a function that fetches HTML content as a string from a file and converts it into an HTML object, which I can see working in the console. However, when trying to assign ...

Navigating the complexities of C++ Linked Lists

For quite some time, I've been grappling with how to create a NODE based link list that has the ability to hold anything through templates. Currently, my Node class is up and running smoothly (tested with my Stack class). However, I'm facing a ...

How to use TypeScript to filter an array based on the values of another array

Suppose I have two arrays. The first one looks like this: names: [{ value: 'recordedData', desc: 'Data' } { value: 'recordedNumbers', desc: 'numbers' } { value: 'recordedNames', desc: 'name ...

The identifier 'name' is not found in the specified data type

How can I resolve the error 'Property 'name' does not exist on type' in TypeScript? Here is the code block : **Environment.prod.ts** export const environment = { production: true, name:"(Production)", apiUrl: 'https://tes ...

Implementing NestJS: Integrating TypeORM Datasource without relying on class dependency injection

I have a unique situation that requires some help. Our team is in the process of integrating nestjs into our current express codebase. Previously, we were using Typeorm 0.2 and recently upgraded to 0.3. Due to the fact that we utilize functions instead of ...

Learn the technique of passing dual onClick parameters to a single function in React

I am working on a function named Test where I need to pass two onClick references. const Test = ({ onConnect }:{ onConnect:any }, { onDisconnect }:{ onDisconnect:any }) => { return ( <div> <DrawDiagram /> <button onClick ...

What is the best way to insert an image within a mat-select-trigger?

How can I dynamically insert two svg flag images into a mat-select-trigger based on the user's language selection? Here is my code for reference: https://i.sstatic.net/SJ8lu.png https://i.sstatic.net/MG2zP.png HTML <div class="select-contain ...

Delete information based on its unique identifier

Currently, I am trying to remove data from a list but encountering a small issue with the following code. Component Code removeSelectedRows(){ console.log(this.selection.selected.map(item => item.userId)) const selectedRowIds = this.selection. ...

Tips for showing various tooltip text when iterating through a list?

I am currently working on a project where I am looping through a list and attempting to assign different tooltip text to various icons. However, I am struggling with the implementation. Here is a snippet of my code: <React.Fragment key={sv.key ...

Is it possible to determine the type of an array value similar to how we can determine the type

Here is something I can accomplish: const keys = { "hi": {name: "ho"} } type U = [keyof typeof keys][0]; // "hi" But can I also achieve the same with array values? const data = [ { name: "hi" } ]; type T = typeof data[0]["name"]; // string not " ...

Generate a new data structure by pairing keys with corresponding values from an existing

Imagine having a type named Foo type Foo = { a: string; b: number; c: boolean; } Now, I am looking to create a type for an object containing a key and a value of a designated type T. The goal is for the value's type to be automatically determin ...

What are the steps for creating a TypeScript version of a custom CKEditor5 build?

Currently, I am in the process of creating a personalized version of CKEditor5. By following the guidelines provided in the official documentation, I successfully obtained ckeditor.js. My goal now is to produce a typescript file (ckeditor.ts or ckeditor.d ...

Show a notification pop-up when a observable encounters an error in an IONIC 3 app connected to an ASP.NET

Currently, I am in the process of developing an IONIC 3 application that consumes Asp.NET web API services. For authentication purposes, I have implemented Token based auth. When a user enters valid credentials, they receive a token which is then stored in ...

Can passing undefined to React's setState method be considered incorrect?

Imagine having the following state declared in a React component: const [selectedUsers, setSelectedUsers] = useState<IUser['id'][]>(); This state is being used as the value for a third party HTML <select /> component from Ant Design. ...

Obtain the property for a shared component using React and TypeScript

How can typescript be informed about the err type? type InputProps = { err?: boolean } export const Input = forwardRef<HTMLInputElement, React.ComponentPropsWithoutRef<'input'>>(({ err, ...rest }, ref) => { // code that uses ...

Positioning a Box tag at the bottom using MUI 5

My goal is to position a Box tag at the bottom of the page. Current Behavior: https://i.stack.imgur.com/ZupNo.png I am looking to place a TextField and send button at the bottom of the page on both browser and mobile. I want user messages to be above th ...

Definition of assignability for intersection types

Exploring the concept of intersection types, we can find a definition in https://github.com/microsoft/TypeScript/pull/3622 When A & B is assignable to X, it means that either A is assignable to X or B is assignable to X. type A = {x:1} type B = {y:1} ...