Enriched Descriptors, maybe ES7 ?
Note that ES6 proxies as specified permits Object.defineProperty/getOwnPropertyDescriptor to pass custom attributes in property descriptors and even passes them along (not sure if anybody has actually implemented this yet) wherever there is a internal get descriptor/set descriptor sequence. Proxies that support custom attributes might be an interest way toi experiment with some of these ideas.
As it is now, Proxies could indirectly benefit from these enriched descriptors.
My current implementation wraps directly Object.create
,
Object.defineProperty
and Object.defineProperties
so that:
function Person(name) {
this.name = name;
this.children = [];
}
Object.defineProperties(
Person.prototype, {
name: {
type: 'string',
writable: true,
enumerable: true,
value: 'unknown'
},
age: {
type: 'number',
writable: true,
enumerable: true,
value: 0
},
children: {
type: Array, // of Persons
writable: true,
enumerable: true
},
birthday: {
returns: 'number',
value: function () {
return ++this.age;
}
}
});
var me = new Person('ag');
me.birthday(); // yay, I'm 1 now!
Ideally, if this would ever be adopted, descriptors should be stored with
custom properties so that Object.getOwnPropertyDescriptor(O, k)
could
pass these around ... well, the good part is that my proposal works out of
the box so even if ignored these properties are just fine as describing the
intent.
Long story short, you add this script on top and if you do this it'll throw Errors:
me.age = me.name;
// throws 'expected number, received ag'
me.children = {};
// throws expected Array received object
The Proxy is able to guard in a similar way but it's an explicit layer/men in the middle while having typed descriptors supporting overloads and returns for function properties go further than just getting and setting properties, i.e.
Object.defineProperty(
window,
'sum',
{
arguments: [
['string', 'number'],
['number', 'number'],
['number', 'string']
],
returns: ['number'],
value: function (a, b) {
return parseFloat(a) + parseFloat(b);
}
}
);
sum(1, '2'); // 3
sum('3', 2); // 5
sum(4, 5); // 9
sum('6', '7'); // throws
Back to 2009, dojo.lang.typed used a similar approach for its own classes definition but that was before ES5 introduced descriptors which are not so commonly used, being very verbose, but absolutely beautifully crafted to describe "classes" properties, IMO.
Having a standardized way to offer this natively and having a drop-in/off polyfill on the way could only, still IMO, benefits in the long term with tools possibly capable to understand type a-la TypeScript in VS and engines could take advantage of these descriptors to boost up with or without proxies around.
I am sure I am missing some case and being an early refactoring of an old proposal I am trying to do it as OK as possible.
Thanks again for your time and thoughts.
Best
Tom has experimented, IIRC. I don't know which implementations preserve extended property descriptor properties.
my quick tests say no implementation does, all seem to create from the scratch a new object with only known/described descriptor properties.
Object.getOwnPropertyDescriptor(
Object.defineProperty(
{},'test',{value:123,type:'number'}
),
'test'
).type // undefined
I'd love to know more about Tom experiments on this!
Best
Andrea Giammarchi wrote:
I'd love to know more about Tom experiments on this!
With his polyfill on the original proxy implementation, now that I think about it.
but originally
es-lab.googlecode.com/svn/trunk/src/proxies/DirectProxies.js
Tom knows all.
Uhm .. that looks just like Proxy and descriptors with known specd properties ... might wait for Tom to know if he had anything to do with types and custom properties in descriptors too.
Although my idea is more about types and less about proxies or descriptors themselves, these are fine as they are now.
Andrea Giammarchi wrote:
Although my idea is more about types
You are starting on the wrong foot there. "Types" mean something in CS. Best if you start with use-cases instead of abusing the term. What code do you want to write, and how exactly would it operate?
As written before, and explained in the project, I can write descriptors with optional extra properties such as:
type, describing as string the typeof, as Function the expected instanceof, or as generic object as prototypeOf(expectedType)
var o = {};
Object.defineProperty(o, 'num', {
value: 0,
type: 'number'
});
o.num = 123; // ok
o.num = '123'; // throws an error, '123' is string, not number
The meaning of type here is the same MS choose for TypeScript but no breaking syntax is necessary plus types can be optionally used to described methods or functions too.
var o = {};
Object.defineProperty(o, 'increment', {
value: function (much) {
this.num += much;
return this;
},
arguments: ['number'], // 1 arg as number
returns: Object // just an instanceof object
});
o.num = 0;
o.increment(1).increment(2);
o.num; // 3
o.increment('1'); // thorws since the argument is not a typeof number
The received arguments can have overloads so that:
var o = {};
Object.defineProperty(o, 'increment', {
value: function (much) {
this.num += parseFloat(much);
return this;
},
arguments: [['number'],['string']],
// 1 arg as number or 1 arg as string
returns: Object // just an instanceof object
});
o.num = 0;
o.increment(1).increment(2);
o.num; // 3
o.increment('1'); // OK
o.num; // 4
o.increment(new Number(1)); // throws, not typeof number/string
Either the amount of arguments is wrong or the type won't match ... the signature is guarded during development time.
In production, you just do not include upfront the proposed script and everything will work as regular ES5 preserving all behaviors and descriptors ... in few words, I am proposing an alternative to TypeScript that integrates well down to ES5.
If engines would like to use types and descriptors provide such info, this could have ideally performance benefits too plus the code will be most likely less "undefined is not defined" prone.
The library I have there, already provides all I've said behind 64 tests that verify the behavior is guarded and preserved with or without the library upfront.
Compatibility with the test link here
Best
about the first example, I forgot the writable for the num property so please read that as if I wrote:
var o = {};
Object.defineProperty(o, 'num', {
writable: true,
value: 0,
type: 'number'
});
the library makes possible to create a partial shim of StructType in few lines of code, using same mechanism to guard types: WebReflection/define-strict-properties/blob/master/src/StructType.js#L40
In this case, types are downgraded to 'number' ... and that's just an example
last link is the test file which better than examples in here describes what happens when the library is around, how ES5 descriptors can guard properties and methods within objects or prototypal inheritance.
I hope what is this about is a bit more clear.
Best
2014-03-09 21:05 GMT+01:00 Brendan Eich <brendan at mozilla.com>:
Tom has experimented, IIRC. I don't know which implementations preserve extended property descriptor properties.
As Allen mentioned earlier, proxies should receive in their defineProperty trap the full descriptor object as it was passed in by the user, including any custom attributes. The proxy is free to store it, and return it from any subsequent getOwnPropertyDescriptor trap (which is also specced so that it can return a non-normalized descriptor object with custom attributes).
Normal (non-proxy) objects of course cannot store non-normalized descriptors, so any custom attributes are lost. Changing this would presumably have a big impact on implementations (which probably don't hang on to descriptor objects at all).
If you want to experiment with custom attributes, my harmony-reflect shim supports this for proxies:
var props = {}; var o = new Proxy({},{ defineProperty: function(target, name, desc) { props[name] = desc; return true; }, getOwnPropertyDescriptor: function(target, name) { return props[name]; } }); Object.defineProperty(o,"p",{value:42, configurable:true, hello:true}) JSON.stringify(Object.getOwnPropertyDescriptor(o,"p")) // prints // {"configurable":true, // "value":42, // "writable":false, // "enumerable":false, // "hello":true } // <- custom attribute preserved
Using Firefox's built-in direct proxies implementation I get a TypeError. I'll investigate further and file a bug.
, Tom
Le 10/03/2014 08:02, Tom Van Cutsem a écrit :
Using Firefox's built-in direct proxies implementation I get a TypeError. I'll investigate further and file a bug.
You already did bugzilla.mozilla.org/show_bug.cgi?id=601379 ;-)
Thanks Tom, I see what you mean but using proxies is a performance killer, while I am trying to propose something that theoretically could also give a performance boost.
I am not proposing custom descriptor attributes here, I am proposing
type
, arguments
(or parameters
) and returns
as specified/extra
descriptor properties capable to bring "typed properties" to, maybe, ES7
My repository already does this and it works even copying objects, if this
is done through getOwnPropertyDescriptor
So, custom descriptor properties a part, would ES7 consider to implement
types not only for StructType
but also for runtime defined shapes so that
Object.defineProperty({}, 'num', {value: 123, writable: true, type: int32})
works same as my polyfill does already?
2014-03-10 11:10 GMT+01:00 David Bruant <bruant.d at gmail.com>:
Using Firefox's built-in direct proxies implementation I get a TypeError. I'll investigate further and file a bug. You already did bugzilla.mozilla.org/show_bug.cgi?id=601379 ;-)
Thanks for refreshing my memory on that one ;-)
But that was actually not the bug that caused my earlier code snippet to fail. Instead it's this bug (already reported by Eddy Bruel): bugzilla.mozilla.org/show_bug.cgi?id=793210
Yesterday, after my DHTMLConf talk, some developer asked me to present/propose my idea about introducing optional types in an ES5 compatible way.
Here a quick overview of properties which aim is to guard types or methods signatures, compatible with overloads for both number of arguments, and acepted type per each argument and each "guarding group".
The return type can be different too, but I didn't forget to include the special type 'any' as white flag.
All types and what they mean are better described here.
The most handy use case is probably
Object.defineProperties(Class.prototype, typedDescriptrs)
since each instance will be automatically guarded inheriting the guarded behavior.As mentioned in this project goals, an ideal/dream goal would be having tools capable of documenting/understanding typed properties such IDE or even "transpilers" and in a parallel universe JS engines capable to natively use such info per each descriptor when available and both speed up properties get/set operations guarding natively the specific type.
As example, this system made possible to implement a StructType shim in few lines of code.
I don't expect this to be discussed soon but I wanted to point at this library now that is just officially born, despite I've proposed something similar in my blog already in 2010, so that if there's any big gotcha/mistake/nonsense in it I am in time to fix/improve or change.
Thanks for your patience reading till the end and for any thoughts or hints that might come out.
Best and happy JS Fest!