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

Select a randomly generated number from an array, which dynamically updates every time the browser is refreshed

I recently completed a project in Angular that utilizes the TMDB API. The project is nearly finalized, but I have a desire to implement a change where the background image (backdrop_path) and other elements shift each time the browser is reloaded. Curren ...

Incorporating Ionic v3 with the latest StripeJS/Stripe Elements version 7.26.0

I have encountered two separate issues while trying to integrate the new version of Stripe into my Ionic v3 app. (Please refrain from suggesting an upgrade to Ionic v5, as it is currently not feasible for our team at this time!) Within my ionDidLoad funct ...

In TypeScript, there is a curious phenomenon where private properties seem to be mimicking the

Here is an example of an issue I encountered while working with private properties in TypeScript. I expected that only the public properties would be visible in my object output, similar to normal encapsulation. My aim here is to include the property wit ...

Encountering a type error with mongoose's pre-save method while using Express with TypeScript

Currently, my tech stack consists of Express.js in TypeScript with Mongoose. Here is the model I am working with: import mongoose, { Schema, Document, Model } from 'mongoose'; import crypto from 'crypto'; import validator from 'val ...

The attribute 'elements' is not present within the data type 'Chart'

var canvas = document.getElementById("canvas"); var tooltipCanvas = document.getElementById("tooltip-canvas"); var gradientBlue = canvas.getContext('2d').createLinearGradient(0, 0, 0, 150); gradientBlue.addColorStop(0, '#5555FF'); grad ...

Looking for a way to make certain pages in Vue3 accessible without authentication using KeyCloak-js

Currently, I am integrating keycloak-js adapter into a Vue 3 Application. The challenge I am facing is with the system's public pages, which prevent me from calling Keycloak immediately. Even though I have set up a button to trigger the login page, th ...

The error type currently displayed relates to window['angularComponentReference']

Currently, I am attempting to incorporate NgZone into my Angular project: constructor( private fishboneService: FishboneService, private zone: NgZone, ) { window['angularComponentReference'] = { zone: this.zone, componentFn: (val ...

Guide to creating numerous separate subscriptions in angular 6

Can you explain the differences between flatMap(), switchmap(), and pipe()? Which one would be most suitable for the given scenario? I need to call the next method once both responses are received. this.jobService.getEditableText('admins', compar ...

Implicated Generic in TypeScript

Is there a way to simplify my class A implementation? export class A<TB extends B<TC>, TC> implements TD<TB, TC> { make(): TC {} } Currently, I have to specify the TC type every time I create an instance of A: class CTest {} class BTes ...

Is there a way to reset static data in a TypeScript subclass? (or alternative method for managing global data)

I have a particular set of static data that I would like to access through an API using basic logic. Specifically, this data pertains to metadata about Java classes. My approach involved incorporating the API into a few static methods within a class, alon ...

Removing AWS-CDK Pipelines Stacks Across Multiple Accounts

Currently, I am utilizing pipelines in aws-cdk to streamline the process of automating builds and deployments across various accounts. However, I have encountered an issue where upon destroying the pipeline or stacks within it, the respective stacks are ...

Tips for validating nominal-typed identifiers

I recently started experimenting with the enum-based nominal typing technique explained in more detail at this link. enum PersonIdBrand {} export type PersonId = PersonIdBrand & string interface Person { id: PersonId firstName: string lastName: ...

Having trouble locating the name 'angular' in TypeScript error message

I have completed all the necessary settings, such as adding the typescript compiler in webstorm and installing tsd with npm. However, I am still encountering an error stating 'Cannot find name Angular'. tsd.json { "version": "v4", "repo": ...

Vue HeadlessUI Error: The function vue.defineComponent is not recognized

Trying to incorporate @headlessui/vue into my nuxt project has been a challenge. My attempt at using it looks like this: <template> <Menu> <MenuItems> <MenuItem>Item</MenuItem> </MenuItems> </Menu&g ...

A class or another interface is the only type that an interface is allowed to extend

We are currently using typescript version 2.9.2 I encountered an issue while trying to extend the interface DropDownOption. I received the error "error TS2312: An interface may only extend a class or another interface." Is there an alternate approach to ...

What is the best way to manage multiple directives from a parent component?

Within my app-landing component, I have implemented multiple typeOut directives. These directives are responsible for gradually writing out text within their respective elements. While this functionality is working as intended, I now seek to exert control ...

Injecting a service into a parent class in Angular 6 without the need to pass it through the constructor

Can anyone provide guidance on how to incorporate a service with multiple dependencies into an abstract class that will be inherited by several classes, in a more streamlined manner than passing it through all constructors? I attempted to utilize static m ...

Waiting patiently for the arrival of information, the dynamic duo of Angular and Firebase stand poised and

Here is the given code snippet: signIn(email, password) { let result = true; firebase.auth().signInWithEmailAndPassword(email, password).catch(error => result = false); waits(100); return result; } I have a ...

Creating a dynamic dropdown menu triggered by a button click using Angular

I am a beginner in Angular (typescript) and I am facing some challenges in adding a new dropdown menu when a user clicks a button. My main struggle is creating additional attribute fields. I'm considering keeping track of the newly added dropdowns wit ...

How can I ensure that a particular component type passes the typescript check in a react-typescript project?

I'm fairly new to using TypeScript, although I have a lot of experience with React (and prop-types). Recently, I've run into an issue when it comes to typing my components, specifically when another component is passed as a prop. I already have ...