Explanation of the idea :
I would like to build a class that can be called like a function without initializing it as an instance. For example, I want to create a class named logger
and use it in two ways: the first way is logger("some text")
and the second way is logger.info("some text")
, using dot notation.
Problems :
The issues I am facing are that I do not want to create a method inside the class that represents the first way, because this method will appear during dot notation. For example:
class logger {
// if i want it to represents the first way: logger("some text")
// this will appear if i write logger.log, and i don't want that.
static log(){}
// this method what i want it to be accessible in object pattern.
// example: logger.info()
static info(){}
}
There is another problem as well:
When I try to access the logger class, it shows me other methods and properties. I want only the methods and properties that I have defined to appear in dot notation. If I, for instance, write logger.
, it will show me apply
and call
and bind
.
One last thing:
I would like to implement the idea using TypeScript.
2
Answers
To implement a class that can be called both as a function and using dot notation without initializing it as an instance, you can use a combination of static methods and a static
call
method. Here’s an example implementation in TypeScript:Let’s break down what’s happening here:
Logger
class with a private constructor, which means it can’t be instantiated as an instance.call
andinfo
. Thecall
method will be used when the class is called as a function, and theinfo
method will be used when accessed using dot notation.instance
that will hold the instance of the class (even though we can’t instantiate it, we need this property to satisfy the TypeScript compiler).Symbol.call
property. This property is a special property in JavaScript that allows an object to be called as a function. By defining a getter for this property, we can control how the class is called as a function.With this implementation, you can use the
Logger
class in both ways:As for the issues you mentioned:
call
method won’t appear when using dot notation because it’s not a part of the class’s prototype.Logger
class using dot notation, you’ll only see the methods and properties that you’ve defined. Theapply
,call
, andbind
methods are part of theFunction
prototype and won’t be visible.Note that this implementation uses a static class, which means it can’t be instantiated as an instance. If you need to create instances of the
Logger
class, you’ll need to modify the implementation accordingly.Quoting my above comment …
The simple reason why using a class is the wrong approach is that class constructors have to be invoked always with
new
, a simple invocation, like the OP is seeking for with the described use case, will immediately throw aTypeError
.A running JS implementation which does exactly what the OP is looking for might look similar to and as straightforward as the following provided code …
The above code as valid, tanspiling/compiling and running TS code can be found at this TS playground.
And regarding following of the OP’s non problem …
This is not a problem, neither of the logger approach/implementation nor of the language itself; it’s just how your IDE works.
A class does manifest itself in a constructor function, thus if you access a class you actually access a special function, and functions have methods too. Every function inherits
call
,apply
andbind
fromFunction.prototype
. Thus these methods are never owned by a function itself, hence they will not show up as own or own enumerable properties. Your IDE is just clever enough to recognize the inheritance/prototype chain and show these methods as available properties.