ForbesLindesay / funtypes

Runtime validation for static types
MIT License
29 stars 4 forks source link

Use named constructor when creating objects #54

Closed pgross41 closed 10 months ago

pgross41 commented 2 years ago

I don't know if this would be functionality to add on the Named type but it would be nice if when creating objects (e.g. via .parse) that it named the object so it was visible in the debugger.

Example functional constructor:

function Rectangle(height, width) {
  this.height = height;
  this.width = width;
}

Once instantiated, the debugger knows it's a Rectangle image

Would it be possible to similarly name funtype objects? The only way I've found to do it dynamically is via new Function:

let Constructor = new Function('height', 'width', `function Rectangle2() {
    this.height = height;
    this.width = width;
  }; 
  return new Rectangle2();`
);

Notice the variable itself is Constructor but the implementation uses Rectangle2 which is a string. The debugger recognizes it as a Rectangle2: image

So in theory whatever process creates the object in funtypes could be put into a string, wrapped in a named function, and passed into new Function? I realize it's probably not that simple but a guy can hope.

The problem with new Function is it is considered an unsafe-eval and will error depending on security headers. image

but maybe there is a better way that I'm not aware of!

pgross41 commented 2 years ago

I found a decent solution using withParser to set the prototype. I made a helper that accepts a function (a named function) and returns a ParsedValueConfig that will set the prototype of the parsed object, giving it the desired name in the debugger and as a bonus it sets the funtype name:

const setPrototype = <T>(func: () => void) => ({
  name: func.name,
  parse: (value: T): Success<T> => {
    Object.setPrototypeOf(value, func.prototype);
    return { success: true, value };
  }
});

Example usage on a funtype object:

const Rectangle = Object({
  height: Number,
  width: Number
}).withParser(setPrototype(function Rectangle(){}));

The object now has a name in the debugger:

Rectangle.parse({height: 1, width: 2});

image

@ForbesLindesay would you be open to a PR to add a .withPrototype helper? For me, it would simplify the call from this:

.withParser(setPrototype(function Rectangle(){}));

to this:

.withPrototype(function Rectangle(){});

and make it easy for everyone else to do it too. It would also open up additional inheritance possibilities if users wanted more than just a name from the source function's prototype.

ForbesLindesay commented 10 months ago

I don't think this is worth the added complexity.