Yet another error: TS2511 - Unable to instantiate an abstract class

My issue is very similar to the one mentioned in this thread: Typescript: instance of an abstract class, however, there are some distinctions. If it is indeed a duplicate problem, I would appreciate a clear explanation as I am currently unable to resolve this issue.

I have a Base class:

export abstract class MyBaseClass {
   private thing: Thing;

   constructor(thing: Thing) {
      this.thing = thing;
   }
}

export type MyBaseClassType = typeof MyBaseClass;
export interface IMyBaseClass extends MyBaseClassType {};

The challenge arises because MyBaseClass is part of a library that I am releasing. Users of this library will create a class derived from MyBaseClass, and the library will serve as an executable solution. They should be able to run their code simply by using the following command:

node our-provided-bin run -b /path/to/client-class-extending-base-class

Within that script, we execute:

const ImportedClassRef: IMyBaseClass = require(resolvedPath);

This ImportedClassRef variable undergoes several methods before we reach the point where we attempt to instantiate it with:

const dude = new ImportedClassRef(thing);

However, I encounter the error message:

TS2511: Cannot create an instance of an abstract class.

Answer №1

Interfaces are simply descriptions of structures and not new named classes.

interface A {
  arg1: string;
}

interface B {
  arg1: string
}

// A and B are essentially the same
let a: A = {arg1: ""};
let b: B = a;

Therefore:

export type MyBaseClassType = typeof MyBaseClass;
export interface IMyBaseClass extends MyBaseClassType {};

is exactly equal to:

type IMyBaseClass = typeof MyBaseClass;

and so your issue is essentially the same as in the question you mentioned.

I am unsure about the type of Thing, but assuming it's a fixed type, I would define it like this:

type Thing = Record<string, any>;

abstract class MyBaseClass {
   constructor( private thing: Thing) {}
}

type InstantiableClass<T extends MyBaseClass> = {
  new(thing: Thing): T
}

class RealClass extends MyBaseClass {} // require(resolvedPath);

const importedClassRef: InstantiableClass<MyBaseClass> = RealClass;

new importedClassRef({}) // no error;

Update: Why does this work?

Abstract classes have a special constructor (playground):

type AbstractConstructor = abstract new(...args: any[]) => any;

const AbstractClass: AbstractConstructor = {} as any;

new AbstractClass() // Error: Cannot create an instance of an abstract class

This constructor is not callable unless called from a derived class. To overcome this, the type InstantiableClass defines a regular constructor that simply returns an instance of T extends MyBaseClass.

type InstantiableClass<T extends MyBaseClass> = {
  /* no abstract */ new(thing: Thing): T
}

Thus, this approach works because it mimics the structure of typeof MyBaseClass while defining a normal constructor instead of an abstract one.

Answer №2

Within a specific class that requires ImportedClassRef as an argument, I structured the properties in this manner:

interface IProps {
   ...
   ImportedClassRef: MyBaseClass | any;
}

export SomeRunnerClass {
    private dude: MyBaseClass;

    constructor({..., ImportedClassRef}:IProps) {
        ...
        this.dude = new ImportedClassRef(thing);
    }
}

Although adding | any solved the issue, it should not be considered a permanent solution. Refer to the feedback provided by semisecure.

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

Troubleshooting Issue with Mongoose Virtual Field Population

I am currently facing an issue with my database due to using an outdated backend wrapper (Parse Server). The problem arises when dealing with two collections, Users and Stores, where each user is associated with just one address. const user = { id: &q ...

What causes a Typescript error when attempting to escape a newline character?

My approach is quite simple: I concatenate multiple strings and format them to make them more readable. info() { return "x: " + this.xpos.toString() + "\n" \ + "y: " + this.ypos.t ...

When trying to save a child entity, TypeORM's lazy loading feature fails to update

I've been troubleshooting an issue and trying various methods to resolve it, but I haven't had any success. Hopefully, someone here can assist me. Essentially, I have a one-to-one relationship that needs to be lazy-loaded. The relationship tree ...

DiscoverField Component with Button Functionality and Checkbox Dilemma

I'm working with Vue 3, Vuetify, and TypeScript for my searchField component. Inside, I have two buttons: FreeItemMaster and PatternMaster. Clicking on these buttons should change their background color and display respective content below them. Howev ...

Discover the most effective method for identifying duplicate items within an array

I'm currently working with angular4 and facing a challenge of displaying a list containing only unique values. Whenever I access an API, it returns an array from which I have to filter out repeated data. The API will be accessed periodically, and the ...

Concealing the Submit Button During Server Processing (Issues with Binding?)

My Angular 2 form is set up to send data to the server asynchronously. I want to provide users with visual feedback during the waiting period by changing the blue 'submit' button to a greyed-out 'Please wait...' button. To achieve this, ...

"Encountered a TypeScript error (2345) when using customElements.define() in Lit 2

When we upgraded various lit-Components to version 2.1.1 "lit": "2.1.1", a TypeScript error surfaced: The argument 'typeof MyComponent' cannot be assigned to a parameter of type 'CustomElementConstructor'. The &apo ...

This property cannot be found on the specified type

So, I have this TypeScript function that will return one of two different objects based on the input value: function myfunc(isError:boolean): {response:string}|{error:string} { if(isError) return {error:''} return {response:''} } N ...

What is the role of the "prepare" function in AWS CDK constructs?

TL;DR: What is the role and purpose of the prepare(): void method in AWS CDK's Construct class? When and how should it be utilized or avoided? The information provided about prepare() states: prepare() function is called after child constructs have ...

Utilizing Next.js and React to interact with Open AI through API requests

I'm currently experimenting with the OpenAI API to develop a chatbot using React, TypeScript, and Next.js. I am facing an issue where clicking the send button in the UI does not trigger any action. Even after adding console.log statements, nothing sho ...

Customize the theme type with @mui/system

Is there a way to customize my theme settings in @mui/system? While using the sx prop in Stack, the theme is defined in createTheme.d.ts, but it seems like there isn't an option to extend or override it. To work around this limitation, I have been u ...

What is the process for exporting a plugin from dayjs() in JavaScript?

Currently, I have incorporated the plugin isToday() to enhance the capabilities of dayjs(). Nevertheless, I am uncertain about how to export isToday() in order to utilize it across other files. import isToday from "dayjs/plugin/isToday"; expor ...

Using `querySelector` in TypeScript with Vue 3

Encountered a TypeScript error while using the Vue Composition API let myElement = document.querySelectorAll('my-element') The TS error I'm getting when trying to access it like this: Property '_props' does not exist on type ...

reading an array of objects using typescript

Trying to retrieve an object from an array called pVal. pVal is the array that includes objects. I am attempting to obtain the value of "group" based on the id provided. For instance, if the id is equal to 1, it should display "VKC". Any assistance woul ...

Is there a method to initiate a 'simple' action when dispatching an action in ngrx?

Here's the scenario I'm facing: When any of the actions listed below are dispatched, I need to update the saving property in the reducer to true. However, currently, I am not handling these actions in the reducer; instead, I handle them in my ef ...

Is it possible to add new values to a GraphQL query?

I am currently utilizing GraphQL and Typeorm in conjunction with an Oracle Database, specifically focusing on retrieving all data from a specific table. The query that is being executed is: SELECT "inventory"."id" AS "inventory_id", "inventory"."value" AS ...

react-data-table-component establishes the structure of the expanded element

Has anyone encountered an issue with the react-data-table-component? I need to pass a row type (typescript model) to the Detail component that is rendered when the row is expanded. Detail: export const Detail = (row: certificates) => { //it works fine ...

What is the most effective method to retrieve the current browser URL in Angular 2 with TypeScript?

Is there a way for me to retrieve the current URL from the browser within my Angular 2 application? Usually in JavaScript, we would use the window object for this task. Can anyone guide me on how to achieve this in Angular 2 using TypeScript? Appreciate a ...

Tips on excluding node_modules from typescript in Next.js 13

I am constructing a website in the next 13 versions with TypeScript, using the src folder and app directory. When I execute `npm run dev`, everything functions correctly. However, upon building, I encounter this error... ./node_modules/next-auth/src/core/l ...

Adding an image to a Select Option label in React: A simple guide

As a newcomer to React, I am experimenting with creating a drop-down menu that includes images in the labels. My approach involves using a function to gather values from a map and construct an id: label pair to display as options in the drop-down. Both the ...