Dynamically Create Instances of ES6 Classes

In a recent project which uses Kotlin multiple platforms support to generate JavaScript classes used in the front-end, I found out that I need to create instances of these classes at runtime. The typical way is to invoke the constructor function directly with arguments. However, this approach has issues when classes have complicated inheritance hierarchy.

Given following TypeScript code with classes Entity and NamedEntity, it’s transpiled to JavaScript code shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Entity {
id: string;
constructor(id: string) {
this.id = id;
}
}

class NamedEntity extends Entity {
name: string;
constructor(id: string, name: string) {
super(id);
this.name = name;
}
toString() {
return `NamedEntity: ${name}`;
}
}

Transpiled JavaScript code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Entity = /** @class */ (function () {
function Entity(id) {
this.id = id;
}
return Entity;
}());
var NamedEntity = /** @class */ (function (_super) {
__extends(NamedEntity, _super);
function NamedEntity(id, name) {
var _this = _super.call(this, id) || this;
_this.name = name;
return _this;
}
NamedEntity.prototype.toString = function () {
return "NamedEntity: " + name;
};
return NamedEntity;
}(Entity));

To create instances of NamedEntity, we usually call new NamedEntity('1', 'abc'). If we only have the reference to the class constructor, we may try to do following to create the instance.

1
2
const ctor = window['my-kotlin-module'].com.company['NamedEntity'];
ctor.call(null, '1', 'abc');

The issue here is that we cannot get the correct value of this argument to use when calling the function call. The result object won’t have the correct prototype chain configured.

To solve this issue, we can use the Function constructor.

In the code below, we dynamically create a function with body containing the new expression to create new instances and invoke this function with arguments. The trick here is to use string template to generate dynamic function body with desired content. Now we can call createNamedEntity('1', 'abc') to create instances of NamedEntity correctly.

1
2
3
4
5
6
7
function createNamedEntity(id, name) {
const ctor = window['my-kotlin-module'].com.company['NamedEntity'];
const func = new Function('ctor', 'id', 'name', `
return new ctor(id, name);
`);
return func.call(null, ctor, id, name);
}
Comments