Enhance the capabilities of a basic object by incorporating a superclass through the creation of

I'm currently developing a library using Typescript 2.0 that can be utilized from both Typescript and JavaScript.

Within the library, there is a class called Component and a function named registerComponent, both written in Typescript.

My goal is to store constructors of classes that inherit from the Component class by calling the registerComponent function. These registered components should then be able to be instantiated automatically by the library. In some scenarios, the method's argument may not be a function but an object. In such cases, I need to convert the object into a constructor function that generates the passed object.

Essentially, I want to inject a superclass into a function responsible for generating objects using Typescript.

This specific section handles raw objects within the registerComponent function.

const newCtor = function() {
  Component.call(this);
};
const properties = {};
for (let key in obj) {
  properties[key] = { value: obj[key] };
}
newCtor.prototype = Object.create(Component.prototype, properties);
return newCtor;

The variable obj represents a plain object provided by the user. Although the code snippet above was intended to work, it resulted in an error

Uncaught TypeError: Class constructor Component cannot be invoked without 'new'
when trying to use the constructor with the new keyword. The exception arises from the line Component.call(this).

How can I create a valid constructor while injecting a superclass?


I apologize for the confusion in my initial post. Now, I believe it's necessary to share the full interface I aim to achieve.

 // Full interface example here
// This section showcases how component registration and instantiation works

class Component{
    public baseFunction():string
    {
        return "This is base";
    }
}
class Registory{
   private static registeredConstructors:{[key:string]:(new()=>Component)};

   public static registerComponent(name:string,c:(new()=>Component)|{[key:string]:any}):void
   {
       if(typeof c === "function")
       {
           Registory.registeredConstructors[name] = c;
           return;
       }else{
           // Assume c is plain object
           // Additional code needed here to wrap c as a constructor 
       }
   }

   public static instanciate(name:string):Component
   {
       return new Registory.registeredContructors[name]();
   }
}

// Example showcasing component registration via Typescript class

class C1 extends Component{
   public someProperty:string = "HELLO C1";

   public f1():string{
      return this.baseFunction() + this.someProperty;
   }
}

Registory.registerComponent("C1",C1);
const c1:Component = Registory.instanciate("C1");

// Example showcasing registering a component using a plain object

Registory.registerComponent("C2",{
    someProperty:"Hello C2",
    f1:function(){
        return this.baseFunction() + this.someProperty;
    }
});
const c2:Component = Registory.instanciate("C2");

// Testing whether c1 and c2 meet expected criteria

test.true(()=>c1 instanceof Component);
test.true(()=>c2 instanceof Component);
test.true(()=>c1.f1() === "This is base Hello C1");
test.true(()=>c2.f1() === "This is base Hello C2");
test.true(()=>c1 instanceof C1);

Answer №1

It appears to me that your specific situation could be resolved more efficiently, given my understanding of it:

abstract class Element {
    constructor(properties: any) {}
}

type ElementConstructor = {
    new (properties: any): Element;
    name: string;
};

const REGISTRY = {} as { [name: string]: ElementConstructor };
function registerElement(ctor: ElementConstructor) {
    REGISTRY[ctor.name] = ctor;
}

function createComponent(name: string, properties: any): Element;
function createComponent<T extends Element>(name: string, properties: any): T {
    if (typeof REGISTRY[name] !== "function") {
        return null;
    }

    return new REGISTRY[name](properties) as T;
}

class CustomElement1 extends Element { }
registerElement(CustomElement1);

class CustomElement2 extends Element { }
registerElement(CustomElement2);

let elem1 = createComponent("CustomElement1", {}); // typeof elem1 is Element
let elem2: CustomElement2 = createComponent("CustomElement2", {}); // typeof elem2 is CustomElement2

(code in the playground)


Update

Alright, now that I have a clearer grasp of what you need, providing assistance is much simpler.
I made some adjustments to your code to align with your requirements:

interface IElement {
    someAttribute: string;
    fun1(): string;
}

abstract class Element implements IElement {
    abstract someAttribute: string;
    abstract fun1(): string;

    public basicFunction(): string {
        return "This is basic ";
    }
}

type ElementConstructor = { new (): Element };

abstract class ElementFromObj extends Element {
    constructor(obj: IElement) {
        super();

        Object.assign(this, obj);
    }
}

class Registry {
    private static registeredConstructors: { [key: string]: ElementConstructor } = {};

    public static addElement(name: string, e: ElementConstructor | IElement): void {
        if (typeof e === "function") {
            Registry.registeredConstructors[name] = e;
        } else {
            Registry.registeredConstructors[name] = ElementFromObject.bind(null, e);
        }
    }

    public static instantiateElem(name: string): Element {
        return new Registry.registeredConstructors[name]();
    }
}
const registry = new Registry();

// Registering components via TypeScript classes

class C3 extends Element {
    public someAttribute: string = "Greetings from C3";

    public fun1(): string {
        return this.basicFunction() + this.someAttribute;
    }
}

Registry.addElement("C3", C3);
const c3: Element = Registry.instantiateElem("C3");

// Registering components via plain objects

Registry.addElement("C4", {
    someAttribute: "Hello from C4",
    fun1: function(){
        return this.basicFunction() + this.someAttribute;
    }
});
const c4: Element = Registry.instantiateElem("C4");

// Testing whether c3 and c4 meet the expected criteria

console.log(c3 instanceof Element);
console.log(c4 instanceof Element);
console.log(c3.fun1() === "This is basic Greetings from C3");
console.log(c4.fun1() === "This is basic Hello from C4");
console.log(c3 instanceof C3);

(code in the playground)

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

Creating an interface that extends the Map object in TypeScript to maintain the order of keys

After learning that the normal object doesn't preserve key order in TypeScript, I was advised to use Map. Nevertheless, I'm struggling to figure out how to assign values once I've declared the interface. Take a look at my approach: Coding ...

The proper way to cancel useEffect's Async in TypeScript

I'm facing an issue with this straightforward example: useEffect(() => { axios.get(...).then(...).catch(...) }, [props.foo]) warning: can't perform a react state update on an unmounted component After some investigation, I found this ...

What is the best way to link multiple select tags in an HTML document?

I am working with a data grid that contains student information such as Name, Class, and Score. Each row has a checkbox for selection. The requirement is that when the user selects one or more rows and clicks on the "show information" Button, a new windo ...

Error: Unable to access the 'filter' property as it is undefined. TypeError occurred

findLoads(){ if(this.loggedInUser.userFullySetupFlag === 0 || this.loggedInUser.businessFullySetupFlag === 0){ swal( 'Incomplete Profile', 'To find loads and bid, all the details inside User Profile (My Profile) and Business Profil ...

Angular Material Table displaying real-time information

Recently, I've delved into Angular and have been experimenting with creating a dynamic table to showcase data. Currently, I have managed to get it partially working with static data. I drew inspiration from this particular example: https://stackblit ...

Exploring the Material Drawer functionality within an Angular application

I'm having trouble integrating the Material Drawer component into my Angular app. Despite following the instructions on https://material.io/develop/web/components/drawers/, it's not rendering properly. Could someone please provide a detailed, s ...

The system encountered a TypeError: Unable to access the 'nativeElement' property as it is undefined

Hello, I am a beginner in Angular 2 and currently working on an image cropping plugin. My goal is to display the image on a canvas element. Below is my HTML code: <div class="row"> <div class="col-sm-6"> <canvas id="layout" width="40 ...

I am trying to figure out how to dynamically set the deployUrl during runtime in Angular

When working with Angular, the definition of "webpack_public_path" or "webpack_require.p" for a project can be done in multiple ways: By setting the deployUrl in the .angular-cli.json file By adding --deployUrl "some/path" to the "ng build" command line ...

Issue with `import type` causing parse error in TypeScript monorepo

_________ WORKSPACE CONFIGURATION _________ I manage a pnpm workspace structured as follows: workspace/ ├── apps/ ├───── nextjs-app/ ├──────── package.json ├──────── tsconfig.json ├───── ...

Setting an attribute on a custom component that is dynamically created within an Angular application

I am working with a custom library component called <my-icon>. To display the icon, I need to specify the property [name] like this: <my-icon [name]='warning'></my-icon> Currently, I am dynamically creating these icons in Type ...

The imported variables are of a union type

In my nextjs project, I developed a customized hook to determine if a specific container is within the viewport using the intersection observer. Here's the code for the custom hook: import { useEffect, useRef, useState } from 'react'; cons ...

Typescript Routing Issue - This call does not match any overloads

Need assistance with redirecting to a sign-up page upon button click. Currently encountering a 'no overload matches this call' error in TypeScript. Have tried researching the issue online, but it's quite broad, and being new to Typescript ma ...

Incorporating ng2-bootstrap into an Angular2 project with SystemJS starter template

I am attempting to integrate the ng2-bootstrap modal functionality into a component of my project that is built using the angular2-starter. Below is my systemjs.conf.js configuration: /* * This config is only used during development and build phase onl ...

How can Angular2 detect when an entity is clicked within a window?

There are multiple items generated using *ngFor: <my-item *ngFor="let item of myArray" [p]="item"></my-item> I am able to handle a click event like this: <my-item ... (click)="doWork(item)"></my-item> However, I want to avoid a ...

Utilizing a forwardRef component in TypeScript to handle child elements

Working with @types/react version 16.8.2 and TypeScript version 3.3.1. This forward refs example was taken directly from the React documentation with added type parameters: const FancyButton = React.forwardRef<HTMLButtonElement>((props, ref) => ...

Efficiently organizing items within a list on Ionic

Currently, I have an ion-list structured as follows: <ion-list *ngFor = "let chat of Chats"> <ion-item (click) = "openChat(chat.id)"> <ion-label> <h2> {{chat.username}} </h2> ...

How can I ensure that a user variable stored in an Angular6 service remains defined and accessible from other components?

Currently, I am working on an Angular app and facing a challenge. After receiving a user variable from an asynchronous call to Firestore Cloud, I noticed that the variable is successfully set (verified using console.log()). However, when I navigate between ...

When coding in JavaScript, the value of "this" becomes undefined within a class function

I'm facing an issue with my TypeScript class that contains all my Express page functions. When I try to access the class member variable using this, I get an 'undefined' error: class CPages { private Version: string; constructor(ver ...

Utilizing Database values in .css files with Vue.js and TypeScript

I am currently working on extracting a color value from the database and applying it to my external .css files. I have searched extensively online but haven't found a satisfactory solution yet. Here is the JavaScript code snippet: createBackgroundHead ...

What's the most efficient way to define the type of an object in TypeScript when one of its properties shares the same name as the type itself?

I'm currently working on processing an event payload where the event field is a string, and the content of data depends on the value of the event field. While I have come up with a representation that functions correctly, I can't help but feel th ...