-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Value properties #2763
Comments
What is the use of this compared to |
Value properties defined using
Food for thought... |
It's valid to call More information about how/when you would expect this to be emitted, and why, would be useful. |
It would be useful for implementing properties that are read-only, similar to the way Consider the following example:
Which emits this JavaScript:
The TypeScript compiler is able to emit accessor properties because of TypeScript's get/set keywords, but it is not able to emit value properties at all. Granted; it is valid to call The only way around this currently is to hand-roll an entire JavaScript class and use TypeScript definition files to work with the JavaScript - which isn't really a great use of TypeScript at all! Consider the following example property syntax, which would not only allow TypeScript to emit value properties, it also allows the syntax for accessor properties to be more succinct than they are currently.
|
There's #12 for read-only in the type system. Can you define "value property" more exactly? Do you mean read-only? A property getter on the prototype of a class? Non-enumerability? Or just that it's important that the property be initialized on the object using |
Lets examine the facts here. If you study the MDN definition of Object.defineProperty you will see that there are two fundamental ways to define properties using
To answer your questions... Definition of "value property" - A property that is defined on an object or an object's prototype, which represents a value as documented in the MDN link above. Do you mean read-only? yes, and no. You can specify A property getter on the prototype of a class? No! This implies the use of an accessor property with only a get accessor function to ensure that the property cannot be assigned to. Non-enumerability? - again, this can be specified using Or just that it's important that the property be initialized on the object using In #12 you have stated "Some properties in JavaScript are actually read-only, i.e. writes to them either fail silently or cause an exception. These should be modelable in TypeScript." - I completely agree, but fundamentally, they are read-only in JavaScript, and value properties are one way (possibly the only way) JavaScript achieves this. It appears from your suggestion that the "readonly" keyword would simply be another TypeScript compiler check, enforcing immutability of values. I am guessing that this would just disappear in the JavaScript, meaning that John Doe who comes along and makes some code that manipulates your immutable code will be able to do just that, since at this point TypeScript isn't there picking up Mr Doe's mistakes. ("value properties" are one way of solving this) |
One option here is to use decorators. decorators allow you to change the definition of the property before it is applied to the class. so your example can look like: function nonConfigurable(target, key) {
// Make the property configurable=false
Object.defineProperty(target, key, {
enumerable: true,
configurable: false
});
}
class Point {
@nonConfigurable
public Empty: Point;
constructor(x: number = 0) {
}
} you can do similar things with enumrable and writable as well. |
Decorators will certainly bridge a gap in functionality, but I don't think this fully solves the issue. Example - defining an accessor property with new syntax:
Emitted code:
Example - defining a value property with new syntax:
Emitted code:
So what have we achieved here?
So far, I have illustrated several compelling reasons why the syntax should change. Given my last point about using lambda syntax for accessor properties, please note that the following code compiles and runs in edge mode!
|
@series0ne, I actually like proposal; i think it fixes the ES separation of get/set that is very confusing in my opinion. |
One quick question - should "enumerable" be somehow related to public/private declaration? So, for example, if a property is declared 'private', enumerable should be set to false. |
The two are not related. Remember that public/private declarations in TypeScript are simply compiler rules. Once the code is compiled to JavaScript, all that goes away; something that is private in TypeScript is actually public in JavaScript. Therefore I think the user should have complete control over The way TypeScript treats public/private is another of my bugbears with the language...see #2940 |
@series0ne , Great Proposal! Simplistic and Sensible... you have my vote. |
+1 for @series0ne 's proposal |
Why limit the accessors to prototypes? In AngularJs, prototype accessors of a directive's controller are called before the controller's constructor. If an accessor needs to use an injected service, you have to abandon that first set call. var TestController1 = (function() {
function TestController1(testService) {
console.log("TestController1 constructor");
this.testService = testService;
}
//This is how TypeScript generates properties.
Object.defineProperty(TestController1.prototype, "aprop", {
get: function() {
console.log("TestController1 Get Value:",this._aProperty);
return this._aProperty;
},
set: function(value) {
console.log("TestController1 setter called with value: ", value);
if(!this.testService){
//in order to make this work I have to exit on the first call.
console.log("TestController1: exiting, no service yet");
return;
}
this._aProperty = this.testService.doSomething(value);
},
enumerable: true,
configurable: true
});
return TestController1;
})(); Instead I can define a property on _this captured in the constructor. In this case the accessor has access to the injected service it needs. var TestController2 = (function() {
function TestController2(testService) {
console.log("TestController2 constructor");
var _this = this;
this.testService = testService;
//This is how I would typically create properties with angular
Object.defineProperty(_this, "aprop", {
get: function() {
console.log("TestController2 Get Value:",this._aProperty);
return this._aProperty;
return _this._aProperty;
},
set: function(value) {
console.log("TestController2 setter called with value: ", value);
this._aProperty = _this.testService.doSomething(value);
},
enumerable: true,
configurable: true
});
}
return TestController2;
})(); Console output:
|
We now have |
I am new to TypeScript and am currently porting my library over to it, but was surprised that class Foo {
readonly value: number;
constructor(value: number) {
this.value = value;
}
} Should compile to: class Foo {
constructor(value: number) {
Object.defineProperty(this, 'value', { value: this.value, enumerable: true, writable: false });
// Some compiler level additional check, that if value is not set by the end of the
// constructor it is set to undefined; this must already happen for default assignment
}
} That way the JavaScript is also safe. Otherwise in JS, a user could easily do Just my 2 cents. I've resorted to using |
@ricmoo that would be against the design goals:
I.e. all type info should disappear upon compilation and should never cause different JS to be emitted. In theory, TS could also emit type validation checks with |
Any further updates to fields should be taken to TC39. |
TypeScript currently supports accessor (get/set) properties, but ECMAScript 5 also allows value properties to be defined.
Object.defineProperty(o, p, { value: n, enumerable: true, configurable: false });
Will TypeScript be able to implement value properties in future?
The text was updated successfully, but these errors were encountered: