When we define a typescript class, we are actually defining two things, a constructor function, and a type. The instances constructed using the constructor defined by a class will have the type of the class.
One might wonder, if it is possible, say from a library, to expose only the type of the class and not the actual type constructor. The goal here may be to prevent direct instantiation of the class, or prevent consumers from monkeying around with the prototype chain of the constructor.
Of course, direct instantiation can be prevented using private constructors, but that is only if the consumers are using typescript. A consumer using plain javascript, or who is liberal with use of any
is more than welcome to instatiate the class directly.
One obvious approach is that we could define an interface, make our class implement the interface and expose just the interface.
This works, but requires quite a bit of extra effort and duplication of signatures (assuming that interface is being created just for this purpose only).
But, what if we could just strip out the run time entity (constructor function) of a class definition, and just retain the type of the class. This is indeed possible and very easy to do.
We can just make an interface which extends a class (and doesn’t add anything extra).
1 2 3 4 5 |
class PersonImpl { name: string; } interface Person extends PersonImpl {} |
Exporting this interface provides our typescript consumers access to the type of the class, but doesn’t provide anyone access to the constructor function or the prototype chain.
If we are not ever exposing any instance of this class, and the class is for internal use only, we are pretty much done here.
However, in case we are exposing instances of this class, then we are not finished, because, any instance of the class also facilitates access to the constructor through the constructor property (which can be overriden) and the prototype chain can be accessed through Object.getPrototypeOf
or the controversial (and legacy __proto__
property.
The safest way to completely hide the implementation of prototype chain while exposing the instances is to wrap the instances in a Proxy that delegates method access and property access to the underlying target instance.