Typescript singleton with method execute - javascript

How is it possible to make a singleton or even normal instance that will force calling a specific method?
For example:
logger.instance().configure({ logs: true });
OR
new logger();
logger.configure({ logs: true });
If calling for a logger without the configure method chained it’ll throw an Error.
Thanks!

Usually when we want to create a singleton, we need to hide constructor. We make constructor private. Why? It is necessary to avoid many instances of classes.
But next question can come to people's mind: "How to initialize variables?". We can do it through configure method:
class MyLogger
{
logs: boolean
private static _instance: MyLogger;
private constructor()
{
}
public static get Instance()
{
return this._instance || (this._instance = new this());
}
public configure({logs}){
this.logs = logs
}
}
const mySingletonInstance = MyLogger.Instance;
And you can configure it like this:
const mySingletonInstance = MyLogger.Instance.configure({logs: true});

Related

How to initialize private property of a class for a test

I'm trying to write a test using JEST to a class I wrote with static properties that resembles the following:
class DataManager {
static #data = null;
static getData = () => {
return this.#data;
}
static initializeData = async () => {
await database(async (db) => {
const data = getSomeDataFromDatabase() //just example
this.#data = data;
});
}
}
Now, I want to make new implementation for my initializeData method, to returned some mocked data instead of going to the db, and then "getData" to see if I get the expected result. The problem is that my #data property is private and I don't want to expose it to the outside world. Is there any way to do it?
No, there is no way to do that. Private fields are really private. If you want to mock them in a test, don't use private fields. Or the other way round: if they're private (an implementation detail), you shouldn't mock them.
You need to either mock the entire DataManager class and its public methods, or if you want to test the DataManager itself then you'd mock the database and getSomeDataFromDatabase functions that it calls.

Object.create() and native private fields

When I create instance of a class via Object.create(Example.prototype) to bypass its constructor, it is no longer possible to work with native private fields:
class Example {
#myPrivateProp;
setup() {
this.#myPrivateProp = 1;
}
}
const e1 = new Example();
const e2 = Object.create(Example.prototype);
console.log(e1.setup()); // works
console.log(e2.setup()); // fails with Uncaught TypeError: Cannot write private member #myPrivateProp to an object whose class did not declare it
Is there a way to make this work, while maintining the invariant of not calling the constructor?
For context you can see this issue: https://github.com/mikro-orm/mikro-orm/issues/1226
What you are defining here is not a private field but a private instance field. A private instance field is there not to be exposed and manipulated by others except that very instance. You shouldn't be able to clone that very instance along with it's state, manipulate it and replace the original one.
You have two options;
A static private field that belongs to the Example class which can be accessed and manipulated mutually by any instance of that class or even by objects created like Object.create(Example.prototype).
such as;
class Example {
static #SECRET = 0;
constructor(){
}
addOne() {
return ++Example.#SECRET;
}
}
i1 = new Example();
i2 = new Example();
console.log(i1.addOne()); // <- 1
console.log(i2.addOne()); // <- 2
i3 = Object.create(Example.prototype);
console.log(i3.addOne()); // <- 3
Example.#SECRET = 4; // <- Uncaught SyntaxError: Private field '#SECRET' must be
// declared in an enclosing class
The private instance field belongs to the instance and can only be accessed and manipulated by that very instance. How this is achieved is exactly seen in your example code. Every single instance will have it's own #myPrivateProp which, if you ask me, is a beautiful thing. For example one can instantiate many individual Queues and their operationaly critical properties wouldn't be exposed.

Conditionally extend the current class

I want a JavaScript class that can conditionally add additional methods into itself from separate files. The idea is to separate different concerns of the app into more manageable self-contained modules that nevertheless can interact with the methods in the mother app class. Therefore, the additional methods in the separate file must be able to reference the methods and variables in the main class. See the code sample below.
I looked at a lot of different solutions, but all of them have a downside for what I'm wanting.
I could do new Uploads(), but I could not find a way for methods in Uploads.js to reference methods in the main App class
I could use extends, but this would not allow for conditional extension AFAIK
I could just define new methods into the prototype itself, but this would mean that the external file would need to "know" about the class it's going to be used in, which doesn't make it widely reusable.
The best I have so far is the following:
app.js
const Uploads = require("./Uploads.js");
const config = { hasUploads: true }; // Probably loaded from a file
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads) {
Object.assign(this, Uploads);
}
}
foo() {
/* Something */
}
}
Uploads.js
module.exports = {
bar() {
this.foo();
}
};
It works, but I don't know if this is the best solution;
There's no constructor, so if Uploads.js needs to do some setup, app.js needs to contain the logic to do so (or at least know to call some uniquely named faux constructor method), which doesn't seem ideal.
If Uploads.js contains a method with the same name as app.js or any other possible module being loaded, they will be overwritten, leading into unexpected behaviour.
The Uploads.js is an object of functions, whereas app.js defines a class. Ideally (though I guess not necessarily) for code manageability they should both use the same syntax.
Is there a better/cleaner/nicer way of doing this?
Instead of trying to perform some kind of crazy multi inheritance, why not try embracing composition? Its very good for solving these kinds of problems.
class App {
constructor(modules) {
if (modules.uploads) {
this.uploads = modules.uploads(this);
}
}
foo() {
console.log('foo!');
}
}
class Uploads {
constructor(context) {
this.context = context;
}
method() {
this.context.foo();
}
}
const app = new App({ uploads: (ctx) => new Uploads(ctx) });
app.uploads.method();
You can get really fancy with this and use builders to configure apps with specific types of modules.
Depending on your anticipated complexity, you might want to think about using event buses, mediators, or commands to decouple things from the host itself.
One option to fix overwriting an existing method from the uploads file is to assign new methods in a loop and check for duplicates (Object.assign is not ideal in this case) and only add updates once:
const Uploads = {
bar() {
this.foo("called from bar");
}
};
const config = { hasUploads: true, // Probably loaded from a file
configured: false
};
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads && !config.configured) {
const proto = this.constructor.prototype;
const methods = Object.keys(Uploads);
methods.forEach( name=> {
if( proto[ name] ) {
throw new Error( "App already has method " + name);
}
proto[name] = Uploads[name];
});
config.configured = true;
}
}
foo(arg) {
/* Something */
console.log( arg );
}
}
const app = new App();
app.bar();
A better (cleaner) alternative might be to add updates to the class before calling its constructor, using a static class method because its this value is the constructor function. Tested example:
static addMethods(uploads) { // inside class declaration
const proto = this.prototype;
for (const [name, method] of Object.entries(uploads)) {
if( proto[name]) {
throw new Error("App already has a ${name} method");
}
proto[name] = method;
}
}
to be called as needed by
if( config.hasUploads) {
App.addMethods( Uploads);
}

Creating an instance of a class to call static methods

I recently came across some code which looks something like this,
class MyClass {
private static instance: MyClass;
private myString: string;
public static Instance(myString) {
if (!this.instance) {
this.instance = new this(myString);
}
return this.instance;
}
private constructor(myString: string) {
this.myString = myString;
}
public getMyString() {
console.log(this.myString);
}
}
My question is, what is the need to do something like this? Why would a person create an instance like this, instead of creating an instance of the class 'the normal way'.
What is the benefit of doing thing like this?
It looks like the Singleton pattern. This pattern is used to ensure that a class has only one instance: the constructor is private, so it cannot be used from the outside of the class.
Regarding this particular implementation, I would suggest a couple of fixes:
this.instance in the static method should be MyClass.instance instead
in the following call new this(myString), myString will be undefined because non static variables cannot be referenced from the static context
there is no way to set myString
public static Instance { ... } should be a static method instead: public static instance() { ... }

Qt programming: How to use custom data type in QVariantMap?

I am writing a Qt app that maps a C++ class to Javascript object in QtWebkit. Firstly let me explain what I am trying to do:
I have a class inherited from QObject:
class myobj : public QObject {
Q_OBJECT
public:
myobj();
~myobj();
pulbic slots:
void getData();
}
And in another class I tried to add myobj instances to QVariantMap:
QVariantMap anotherClass::getObj() {
myobj* obj1 = new myobj();
myobj* obj2 = new myobj();
QVariantMap items;
items.insert(QString("0"), QVariant(*obj1));
items.insert(QString("1"), QVariant(*obj2));
return items;
}
And then I got the following error:
error: no matching function for call to ‘QVariant::QVariant(myobj&)’
So I tried to add declarations:
Q_DECLARE_METATYPE(myobj);
But I got:
error: ‘QObject::QObject(const QObject&)’ is private
Any idea about this?
Like the compiler said, no constructor of QVariant exists that take a myobj as parameter. Have you tried to use the qVariantFromValue function instead?
I think this is what you are searching for.
If you register your custom type with Q_DECLARE_METATYPE(myobj), your class needs a public default constuctor (ok), a public destructor (ok) and a public copy constructor (MISSING which the error message is telling you), see the documentation.

Categories