I have a case where I want a callable object, or at-least the feeling of:
{}()
{}.prop
I am not the biggest fan of classes in the world but I have to admit it they have been getting some special treatment of late so in my case its unavoidable. private fields, class field and more. Anyway to achieve the following in a non hacky simple kind of way we need to do this:
Worth a note that this is typescript but should work in JavaScript too.
class Dog {
static legs = 5;
constructor() {
console.log('woof');
}
}
// progmatic use of `new` via .construct
// preload the first argument with the class we want to call;
// proxy the actual Reflect.construct method but point all gets and sets to the static Class constructor, in english: makes static available NOTE this does not mess with Reflect.construct
const callableObject = new Proxy(
Reflect.construct.bind(null, Dog),
{
get(tar, prop, val) {
// access static
return Reflect.get(Dog, prop, val);
},
set(tar, prop, val) {
// access static
return Reflect.set(Dog, prop, val);
},
apply(target, thisArg, argumentsList) {
// make the constructor work
return target({...argumentsList, length: argumentsList.length});
}
}
);
callableObject(); // calls constructor
callableObject.legs; // 5
magic :)
Top comments (5)
Reflection is always so magic! I've used them in a couple of situations in my code, and when I come back to them, I have trouble to understand what's happening because it's too magical to remember ¯_(ツ)_/¯
I know thats the problem, they are not idiomatic. I tend to write libraries, lots of libraries and I always want to give the best experience for developers, usually thats why my libraries never get finished. So I do usually avoid magic, the thing is I am doing code generation via typescript and reflection and so I needed to do this stuff.
The above code is a snippet from my abstraction over Regl, I am attempting to create a reactive webgl framework in the style of Vue.js.
Reactive webgl, that sounds like a great idea! I hope you share it!
I completely see the point of reflection within libraries, abstracting their "magic" from the developers that use them and offering features that weren't possible before.
I struggled when I use reflection for the business logic... I just won't do it anymore :D
I highly recommend writing a helper function which returns an instance of the class. This lets you exactly control the return type. For example, that's how I prefer to do private fields/methods/etc.:
To keep typings do this (@ts-ingore is required but everything still works):