It is not possible to instantiate an object of a class specified by a generic parameter

As a Java developer who is grappling with Typescript, I am faced with a challenge in implementing an abstract class called Shape and two concrete classes (Square and Circle) that inherit from it. My goal is to hide the constructor for these classes and instead create a factory method named getNewShape. This method will determine which concrete constructor to use based on an argument provided. In Java, achieving this would be straightforward by writing the following code snippet inside the Shape class:

public static <T extends Shape> T getNewShape(T reference){
    // my logic
    reference = T.class.getInstance();
    return reference;
}

In Typescript, I would like to implement it as follows:

let myShape = Square.getNewShape(myShape);

I have come across suggestions to handle this issue using the following approach:

For the consumer:
let myShape = Square.getNewShape(myShape, Square);

Inside the Shape class:
public static <T extends Shape>(ref: T, type): T{
   //my logic
   reference = new type();
   return reference;
}

However, I would prefer if TypeScript could infer the Type parameter automatically when calling getNewShape from the concrete classes, without needing to explicitly specify it in the parameters.

Update: Including the complete code sample below:

    class Shape {
    constructor(){

    }

    public static getNewShape<T extends Shape>(ref: T, type: new() => T): T{
        if(ref) {
            ref.clear();
        }
        ref = new type();
        return ref;
    }

    protected clear(){
        console.log('clearing');
    }
}

class Circle extends Shape{
    protected constructor(){
        super();
    }
}

class Square extends Shape{
    protected constructor(){
        super();
    }
}

class App {
    private _circle: Circle = null;
    constructor(){

    }

    public setup(){
        this._circle = Circle.getNewShape(this._circle, Circle);
    }
}

Answer №1

Here is a potential solution to move forward:

abstract class Shape {
  public static getNewShape<T extends Shape>(
    this: { prototype: T },
    ref: T
  ): T {
    if (ref) {
      ref.clear();
    }
    
    ref = new ((this as any) as new () => T)();
    return ref;
  }

  protected clear() {
    console.log("clearing");
  }
}

class Circle extends Shape {
  private constructor() {
    super();
  }
  radius: number = 0;
}

class Square extends Shape {
  private constructor() {
    super();
  }
  side: number = 0;
}

class App {
  private _circle: Circle = null!;
  
  constructor() {}

  public setup() {
    this._circle = Circle.getNewShape(this._circle);
    Square.getNewShape(this._circle);
  }
}

The critical points here are:

  • When a constructor is marked as protected or private, the restrictions are quite strict. If the constructor of Circle is not public, it cannot be called inside Shape.getNewShape() because protected constructors are only accessible from subclasses, not superclasses. Type assertions can override the compiler's complaints inside the implementation of Shape.getNewShape(), but this is not type-safe.

  • To work around issues with private or protected constructors, instead of using new()=>T, we can utilize the strongly-typed prototype properties of class constructors in TypeScript, which are also of type T. So, {prototype: T} is used instead. We need to ensure that Square and Circle don't encounter runtime issues when instantiated with no arguments due to the preceding type assertion.

  • Since you are calling a constructor's static method within SomeShape.getNewShape(), the context for that call should be the constructor itself. Therefore, new this() is employed. A this parameter prevents users from calling the method on the wrong constructor.

Additional considerations include:

  • I added properties to Circle and Square to differentiate them structurally from each other and from Shape and an empty class. This is essential in TypeScript to avoid unexpected outcomes since types are recognized by shape rather than name.

  • If constructors are inaccessible, obtaining the first valid reference to an instance of Square or Circle presents challenges. Using null as a valid instance of

    Shape</code may require adjustments in your logic and potentially utilizing compiler options like <code>--strictNullChecks
    .


In conclusion, I recommend reconsidering hiding constructors if they are exposed via a static method. Instead, exposing constructors as public and incorporating necessary functionality directly into the abstract Shape constructor may simplify your code and align better with TypeScript conventions.


I hope this guidance proves helpful. 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

The Vercel/NextJS deployment does not delay the completion of the serverless function until the email is sent via Azure

Upon a user's registration, I am attempting to send a registration/account activation email. While the email sends successfully via Azure's email services when running on localhost, deployments on Vercel do not trigger the email (although the use ...

Modify the data type of an object member based on its original type

I am seeking to convert the data type of each member in an object based on the specific member variable. Here is an example: class A { fct = (): string => 'blabla'; } class B { fct = (): number => 1; } class C { fct = (): { o ...

Implementing HTTP GET and POST requests in Angular 2 allows for the functionality of displaying, adding, and deleting objects within a list

Hey there, I'm completely new to dealing with HTTP and fetching data from the server. I've been scouring through various tutorials, examples, and questions on different platforms, but unfortunately, I haven't been able to find exactly what I ...

The NextJS image URL remains constant across various endpoints

Currently experiencing an issue with my Channel Tab where the icon and name of the channel are displayed. The problem is that every time I switch channels, the icon remains the same as the first channel I clicked on. PREVIEW This is the code I am current ...

Include TypeScript in a single component within an already established Vue project

I've been working on a large Vue project and I want to integrate TypeScript into it. However, every time I try to do so, I run into a lot of errors that take weeks to fix. Instead of going through that, I'd like to find a way to add TypeScript to ...

Navigating TS errors when dealing with child components in Vue and Typescript

Recently, I encountered an issue where I created a custom class-based Vue component and wanted to access its methods and computed properties from a parent component. I found an example in the Vue Docs that seemed to address my problem (https://v2.vuejs.org ...

One important rule to remember when using React Hooks is that they must be called consistently and in the correct order in each render of the component. It

Encountering an issue while trying to use the ternary operator to determine which React Hook to utilize. The error message states: "React Hook "useIsolationDynamicPhase" is called conditionally. React Hooks must be invoked in the same order during every co ...

GraphQL type featuring a union property defined inline

Trying to create a matching GQL type for this Typescript interface: interface List { items: [String] | [[String]] } Initially, I attempted to keep it straightforward: type List { items: [String]! | [[String]!]! } However, GQL did not approve of tha ...

Utilize SWR in NextJS to efficiently manage API redirection duplication

When using SWR to fetch data, I encountered an error where the default path of nextjs was repeated: http://localhost:3000/127.0.0.1:8000/api/posts/get-five-post-popular?skip=0&limit=5 Here is my tsx code: 'use client' import useSWR from &quo ...

challenges with template inheritance: when one template takes precedence over another

I have encountered an issue with my two HTML templates, login.html and signup.html. Both of these files inherit from the base.html file, but there seems to be a problem where one file is overriding the title and content of the other. So when I visit /login ...

Error message: "react-router typescript navigation version 0.13.3 - Unable to access 'router' property"

I'm currently in the process of porting an existing React project to TypeScript. Everything seems to be going smoothly, except for the Navigation mixin for react-router (version 0.13.3). I keep encountering an error message that says "Cannot read prop ...

Encountering an error while compiling the Angular 8 app: "expected ':' but got error TS1005"

As I work on developing an Angular 8 application through a tutorial, I find myself facing a challenge in my header component. Specifically, I am looking to display the email address of the currently logged-in user within this component. The code snippet fr ...

When using ngFor in HTML, the ID of an object within an array of objects can become undefined during execution

Currently, I am in the process of developing a mobile application with the Ionic framework. One of the key functionalities of this app involves making an API call to retrieve transaction data, which then needs to be displayed in the HTML template: The dat ...

Enhance your React application by using a personalized hook that allows you to trigger a function

After creating a custom hook to handle uploads to an AWS S3 bucket, I encountered a small issue. Rather than having the hook execute the logic directly, I decided to create an executable function to return instead. However, I am facing a problem where the ...

Is it possible to pass values to an object of an extended class?

Uncertainty creeps in as I ponder a potentially foolish idea (possibly recursive): I find myself tasked with building two objects using the same parameters. One object features a shared method, while the other boasts two private methods (think of them as p ...

Using VueJs and typescript, transform the input image into Base64 format

Welcome to my first question on this platform! I am looking for a way to convert an input file (image) from a form using VueJs 3 and typescript to Base64 in order to "send" it to my backend (java, spring boot) and store it in mongodb as part of a "User" m ...

Codify the mathematical operation using Typescript

While working on my Angular 2 project with TypeScript, I encountered an issue where I am attempting to send an arithmetic operation such as 2+4 through an http get request. The expected response from the back-end should be 6. However, the problem arises w ...

Transmit information between components through a form

Is there a way to transfer data from one component to another in Angular? I have two components set up and I am currently using a selector to display the HTML content in the first component. Now, I need to figure out how to send the data entered in a form ...

What is the recommended TypeScript type to be returned from a GraphQL resolver when using ESLint?

Repository Link https://github.com/inspiraller/apollo-typescript The code is functioning correctly, however, Eslint typescript is raising complaints. An eslint error occurs on the following code block: Query: { players: () => players } Miss ...

React encountered an issue: each child element within a list must be assigned a unique "key" prop

I am feeling a bit puzzled as to why I keep getting the error message: child in a list should have a unique "key" prop. In my SearchFilterCategory component, I have made sure to add a key for each list item using a unique id. Can you help me figu ...