Dynamically Loading a Typescript Class (Reflection for Typescript)

Dynamically loading a typescript class (reflection for typescript)

You could try:

var newInstance = Object.create(window[className].prototype);
newInstance.constructor.apply(newInstance, instanceparameters);
return newInstance;

Edit This version is working using the TypeScript playground, with the example:

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}

//instance creation here
var greeter = Object.create(window["Greeter"].prototype);
greeter.constructor.apply(greeter, new Array("World"));

var button = document.createElement('button');
button.innerText = "Say Hello";
button.onclick = function() {
alert(greeter.greet());
}

document.body.appendChild(button);

Dynamically generate and return a class object

There are several possible solutions, it depends how much "automated" the solution should be.

1) Simple

Using mapping, similar to AdityaParab's answer.


class Product1 {}
class Product2 {}
class Product3 {}

// Mapping from type name to class object.
const ProductMap = {
prod1: Product1,
prod2: Product2,
prod3: Product3
};

function getProductByType(type) {
const Product = ProductMap[type];
if (!Product) throw new Error(`Unknown ProductType '${type}'.`);
return new Product(type);
}

console.log(getProductByType("prod1"));

2) Real Reflection (requires TypeScript transformer)

StackBlitz demo here.

Check out tst-reflection GitHub repo.

Decorator used to mark Product classes.

/**
* Decorator used to mark Product classes.
* @reflect - required JSDoc property. Means all decorated types can be used in reflection.
* @param productType
*/
export function ForProductType(productType: string) {
return (ctor: Function) => {};
}

Decorated Product class.

@ForProductType('prod1')
export class Product1 implements Product {
readonly type: string = 'prod1';

get name() {
return 'Product 1';
}
}

getProductByType function with a little bit of reflection.

// Some reflection job.. Find all types decorated by the ForProductType decorator and create map of those types.
const entries = Type.getTypes()
.map<[string, Type]>((type) => [
type
.getDecorators()
.find((decorator) => decorator.name == 'ForProductType')
?.getArguments()[0],
type,
])
.filter(([typeName, type]) => !!typeName);
const ProductTypeMap = new Map<string, Type>(entries);

function getProductByType(type: string): Promise<Product> {
const ProductType: Type = ProductTypeMap.get(type);

if (!ProductType) {
throw new Error(`Unknown ProductType '${type}'.`);
}

return ProductType.getCtor().then((Product) => new Product());
}

Usage

getProductByType('prod1').then((product) => console.log(product, product.name));

It returns Promise cuz it does dynamic imports of the Product classes.

Typescript reflection inside a namespace

Hah, asking the question led me to see the light. I think. The trick was to export the classes, duh.

namespace Test {

interface INamed {
name:string;
}

export class Foo implements INamed {
name:string;
constructor( name:string ) {
this.name = name;
}
}

export class Bar implements INamed {
name:string;
constructor( name:string ) {
this.name = name;
}
}

export function factory( className:string ):INamed {
return new Test[className]( className + " instance" );
}

}

var bar = Test.factory( "Bar" );
console.log( bar.name );

How to get a class from a string in TypeScript/JavaScript in an Angular 2 application?

This sounds like a reflection question in typescript.

Possibly you could check this out if you haven't

Dynamically loading a typescript class (reflection for typescript)

Angular2 typescript create new object dynamically with class name from variable?

There are a few ways to do this. If your class is in a separate module:

SomeClass.ts

export class SomeClass {

constructor(arg: string) {
console.log(arg);
}
}

App.ts

import * as s from "./SomeClass";

var instance = new s["SomeClass"]("param");

Or using namespaces:

namespace Test {

export class SomeClass {

constructor(arg: string) {
console.log(arg);
}
}
}

var instance = new Test["SomeClass"]("param");

javascript dynamically create class instance from a class name

Use the apply invocation pattern or Function.prototype.call :

function createBlock (divName, className){
var myDiv = document.createElement("div")
myDiv.id = divName;
document.body.appendChild(myDiv);
var block = className.call(null, myDiv);
return block;
}

This will require both constructor functions, Header and Footer, to be scope-safe by checking for this

function Header(arg) {
if(this instanceof Header) {
//initialise
return this;
}
else {
return new Header(arg);
}
}


Related Topics



Leave a reply



Submit