I will have a new question. Previously, I used a vanilla javascript project. In this project, we made "object" by modifying the prototype. This code was not optimal, I am aware of it, but it worked.
Technologies evolve and so do people. Now, I would like to use the notion of class and Typescript. I created a whole set of classes that correspond to the old ones, but with new features and cleaner code. I would like to know if I can make the existing cohexist exist with the new code (without touching the code).
Here is my old code : https://codepen.io/Answerrer/pen/YzyeNpe
function declareClass(classModel, version) {
if (typeof classModel !== 'function') {
throw classModel + ' cannot be used as a class as it is not a function';
}
if(window.myClasses === undefined) {
window.myClasses = {};
}
window.myClasses[classModel.name] = classModel;
classModel.prototype._version = version || '1.0';
classModel.getVersion = function() {
return this.prototype._version;
};
};
function declareChildClass(subClass, superClass, version) {
declareClass(subClass, version);
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
subClass.prototype._super = superClass.prototype;
};
(function() {
let proto = a.prototype;
window.declareClass(a, 'A1')
function a(id, properties) {
this.id = id;
this.properties = properties;
}
proto.echo = function() {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
}
})();
(function() {
window.declareChildClass(b, window.myClasses.a, 'B2')
function b(id) {
window.myClasses.a.call(this, 'B' + id, {})
}
})();
var a = new window.myClasses.a('TestA', {data: 'test'}),
b = new window.myClasses.b('X');
a.echo();
b.echo();
Here is the code for class A that I recoded. I would like to use this class without touching up the code I have in object B. The code of the declareClass and declareChildClass functions can be modified.
export default class A {
id:string;
properties:{};
constructor(id:string, properties:{}) {
this.id = id;
this.properties = properties
}
echo() {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
}
}
If anyone has an idea, I'm interested. If this is not explicit enough I will try to give more information.
Thank you so much.
So I've tested replacing your ES5 old prototype base a
function with new ES6 class a
. It raise the error:
Uncaught TypeError: Class constructor a cannot be invoked without 'new'
Expand this snippet to test:
function declareClass(classModel, version) {
if (typeof classModel !== 'function') {
throw classModel + ' cannot be used as a class as it is not a function';
}
if(window.myClasses === undefined) {
window.myClasses = {};
}
window.myClasses[classModel.name] = classModel;
classModel.prototype._version = version || '1.0';
classModel.getVersion = function() {
return this.prototype._version;
};
};
function declareChildClass(subClass, superClass, version) {
declareClass(subClass, version);
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
subClass.prototype._super = superClass.prototype;
};
(function() {
class a {
constructor(id, properties) {
this.id = id;
this.properties = properties
}
echo() {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
}
}
window.declareClass(a, 'A1')
})();
(function() {
window.declareChildClass(b, window.myClasses.a, 'B2')
function b(id) {
window.myClasses.a.call(this, 'B' + id, {})
}
})();
var a = new window.myClasses.a('TestA', {data: 'test'}),
b = new window.myClasses.b('X');
a.echo();
b.echo();
It originates from this line: window.myClasses.a.call(this, 'B' + id, {}).
Because myClasses.a
is a ES6 class, it doesn't support being called without new
keywords. Quite unfortunately there's no workaround for this issue right inside vanilla JS.
But since you're using TS, you can set the transpile target to ES5
to force converting the shiny class
syntax back to old school prototype base function. The result will be:
var a = /** @class */ (function () {
function a(id, properties) {
this.id = id;
this.properties = properties;
}
a.prototype.echo = function () {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
};
return a;
}());
And that error is gone, expand this snippet to test:
function declareClass(classModel, version) {
if (typeof classModel !== 'function') {
throw classModel + ' cannot be used as a class as it is not a function';
}
if(window.myClasses === undefined) {
window.myClasses = {};
}
window.myClasses[classModel.name] = classModel;
classModel.prototype._version = version || '1.0';
classModel.getVersion = function() {
return this.prototype._version;
};
};
function declareChildClass(subClass, superClass, version) {
declareClass(subClass, version);
if (typeof superClass !== 'function' && superClass !== null) {
throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
subClass.prototype._super = superClass.prototype;
};
(function() {
"use strict";
var a = /** @class */ (function () {
function a(id, properties) {
this.id = id;
this.properties = properties;
}
a.prototype.echo = function () {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
};
return a;
}());
window.declareClass(a, 'A1')
})();
(function() {
window.declareChildClass(b, window.myClasses.a, 'B2')
function b(id) {
window.myClasses.a.call(this, 'B' + id, {})
}
})();
var a = new window.myClasses.a('TestA', {data: 'test'}),
b = new window.myClasses.b('X');
a.echo();
b.echo();
Conclusion, yes you can mix TS code with old JS code and make them work together. But you need to change the TS code a bit to align with some of the old paradigm, like you don't use module in old code, just global variables. You might want to ditch module in TS too.
class a {
id:string;
properties:{};
constructor(id:string, properties:{}) {
this.id = id;
this.properties = properties
}
echo() {
console.log('ID [' + this.id + ']');
console.log('Properties');
console.log(this.properties);
}
}
;(window as any).declareClass(a, 'A1');
Or, if you still prefer using module, then you need a bridge code on TS side.
bridgeCode.ts
import a from './myClassA.ts';
;(window as any).declareClass(a, 'A1');