You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ES6 allows an arbitrary expression as the name of a property. The syntax is like this:
varx={["some"+"arbitrary"+"name"]: 0}
Here is my current proposal for supporting this feature:
Computed expressions would be allowed in the following places if target is ES6:
Object literal properties
Class methods and accessors (both static and instance), but not property declarations.
'this' references are not allowed in computed properties.
No parameter properties
Do not allow in interfaces
No enum members
Below ES6:
Computed properties are not allowed
Note that emit for all of the above is straightforward.
Type check:
All computed names must be of type string, number, or any (or eventually symbol)
No property will be added to the containing type based on a computed name.
Computed names that are not well known do not add a property to the type, but they will be included in the union type for the indexer, if the containing type has an indexer.
Questions:
Should an unknown name cause the surrounding type to have an indexer, if it wouldn't have acquired one otherwise? Our decision here is 'no'. Here is an argument for and against:
Argument for - we would want this to work:
varn="some "+"unknown "+"name";varobj={[n]: 0;};varnum=obj[n];// This should be number
Argument against - Computed properties would be allowed in class properties, so someone might have a class hierarchy like this:
varn="some "+"unknown "+"name";classC{[n]: number;}classDextendsC{foo: string;// Error because C was given an implicit indexer that D inherited, but foo doesn't conform to the indexer}
I think the down-level should be apply or not apply to both classes and object literals. However, down-level one but not the other seems confusing and asymmetric to me.
Some question
For the enum member, is there a reason why we won't support the feature or is it just not part of ES6 spec?
for below ES6, allowing in an interface will only be a well known name as well ? In the case, how we gonna emit them?
Yui (@yuit) Your point about the symmetry of down leveling is well taken.
To answer your questions:
Firstly, enums are not in ES6, so this decision is owned completely by us. Enum members with constant values are inlined though, and our inlining requires us to know exactly which member we are referencing. Computed names would produce an enum member that cannot be inlined. Plus, it is really not useful to have a computed name in an enum.
We never emit interfaces, so we would never emit the computed names of interfaces. Therefore, it is pointless to have a computed name in an interface, unless it has a well known value and can contribute to the type of the interface. This is independent of which EcmaScript version we are targeting.
interfaces, we only have .d.ts to worry about, and for these, you emit them as is, but ensure that all identifiers in the expression are exported as well.
varC=(function(){var_instanceProperty;functionC(){this[_instanceProperty]="i";}C[staticProperty]="a";_instanceProperty=instanceProperty;// capture the value of the expressionC[staticMethod]=function(){};C.prototype[instanceMethod]=function(){};Object.defineProperty(C.prototype,instanceAccessor,{get: function(){return0;},enumerable: true,configurable: true});Object.defineProperty(C.prototype,staticAccessor,{get: function(){return0;},enumerable: true,configurable: true});returnC;})();
Notes:
A computed property has to be initialized, a non initialized computed property is considered an error
'this' is not allowed in computed properties expression
Emitting properties wither static or instance needs to happen in the order of declaration, to allow for using a previous member of the static side of the class as a key
Well known computed names (like string literals, number literals, and eventually symbols) will cause a property to be added to the type that contains it. Example:
What is the value of enabling this?
// This looks sillyvarx={["name"] : 0}
-Do not allow in interfaces, unless it is a well known name (#980)
This should also include object type literals
Computed names that are not well known do not add a property to the type, but they will be included in the union type for the indexer, if the containing type has an indexer.
I am not sure i understand that, do you mean "checked against"?
Why add a property for well known names: This is more of a precursor to the symbol work. The idea is that if we have full symbol support, then when the user references a well known symbol, we want to add a property. I am just generalizing that to apply to any well known name, symbol or not. However, I am open to the idea that this generalization might not be useful, and we can decide to skip it. Because without symbols, it is pretty pointless. I will add that if we do not add this generalization, then we should not support computed names at all in interfaces / object types (even well known ones).
Good point about disallowing 'this' in computed properties in classes. Unless we decide that the instance side computed properties can be evaluated in the constructor, but that is not the current design.
I do not think we have to forbid uninitialized computed properties in a class. We can still emit the expression, and assign it to a local var in the class closure. The reason this is useful is that when we have a well known name, we would still want to add the property to the type even if the user is not ready to initialize the property yet.
If an indexer type is computed as the union of properties, we include the property type in the union. If the indexer already exists, we have to check the computed properties against the indexer type.
We may want to only support adding a property for well known names for symbols and constant enum members. The string and number literal case is truly useless.
We will allow only well known names in uninitialized class properties (both instance and static). We will basically allow the same thing as we allow in an interface.
Here is an interesting question. One thing that seems apparent is that we should check that the computed properties in a class conform to the indexer declared by that class. It seems equally natural to do this if the indexer is inherited. The question is, what about inherited computed properties? These properties never actually appear on the type, so one can argue that they do not get inherited. On the other hand, we know they are there!
This would work in any expression context and we already have the infrastructure in place to track and emit the temporary variable declaration (from my work on downlevel destructuring).
Can we have an explanation as to why we disallow 'this' ? I believe the following code is valid ES6 and it worked in my limited testing (only tested chrome using the console).
I can understand this may not be possible in some contexts such as member initialisers or interfaces, however why is such usage blocked when composing objects within functions?
Thanks Kagami Sascha Rosylight (@saschanaz). After some investigation it seems that this error is coming from ReShaper. I initially ruled our ReSharper as there was no options to ignore the error or configure the inspection severity. I'll open a new ticket with them.
Activity
yuit commentedon Nov 7, 2014
I think the down-level should be apply or not apply to both classes and object literals. However, down-level one but not the other seems confusing and asymmetric to me.
Some question
JsonFreeman commentedon Nov 7, 2014
Yui (@yuit) Your point about the symmetry of down leveling is well taken.
To answer your questions:
mhegazy commentedon Nov 7, 2014
Here is a proposal for how to emit them:
results in:
Notes:
mhegazy commentedon Nov 7, 2014
A few more notes:
What is the value of enabling this?
This should also include object type literals
I am not sure i understand that, do you mean "checked against"?
JsonFreeman commentedon Nov 8, 2014
JsonFreeman commentedon Nov 8, 2014
Also, I don't think the following emit will work:
Recall that a comma in a variable statement actually separates variable declarations from each other, so you need to use semicolons instead:
JsonFreeman commentedon Nov 8, 2014
Some updates:
JsonFreeman commentedon Nov 12, 2014
I'm updating this with the following changes:
JsonFreeman commentedon Nov 14, 2014
Updating a few things:
I will update the proposal above to reflect these.
JsonFreeman commentedon Jan 15, 2015
Here is an interesting question. One thing that seems apparent is that we should check that the computed properties in a class conform to the indexer declared by that class. It seems equally natural to do this if the indexer is inherited. The question is, what about inherited computed properties? These properties never actually appear on the type, so one can argue that they do not get inherited. On the other hand, we know they are there!
JsonFreeman commentedon Jan 26, 2015
Assigning to Anders Hejlsberg (@ahejlsberg) for spec integration.
danquirk commentedon Jan 26, 2015
Could we just close this and make a new issue for spec work if this one was just to track the dev work?
JsonFreeman commentedon Jan 26, 2015
Opened a separate issue to track the spec work.
ahejlsberg commentedon Jan 28, 2015
Jason Freeman (@JsonFreeman) Mohamed Hegazy (@mhegazy) Regarding downlevel emit for computed properties, for the example
we could use temporary variables and the comma operator to emit
This would work in any expression context and we already have the infrastructure in place to track and emit the temporary variable declaration (from my work on downlevel destructuring).
JsonFreeman commentedon Jan 28, 2015
Yes, that is a good idea. I will open two issues, one for downlevelling computed properties, and one for allowing them in class property declarations.
JsonFreeman commentedon Jan 28, 2015
Anders Hejlsberg (@ahejlsberg) I opened #1840 and #1841
noggin182 commentedon Aug 9, 2016
Can we have an explanation as to why we disallow 'this' ? I believe the following code is valid ES6 and it worked in my limited testing (only tested chrome using the console).
I can understand this may not be possible in some contexts such as member initialisers or interfaces, however why is such usage blocked when composing objects within functions?
saschanaz commentedon Aug 9, 2016
Matthew Gregory (@noggin182)
The following code is valid TypeScript. Maybe it was fixed after the original proposal.
PS: It seems the
thisreference restriction is only for classes.noggin182 commentedon Aug 9, 2016
Thanks Kagami Sascha Rosylight (@saschanaz). After some investigation it seems that this error is coming from ReShaper. I initially ruled our ReSharper as there was no options to ignore the error or configure the inspection severity. I'll open a new ticket with them.
Update: Reported here: https://youtrack.jetbrains.com/issue/RSRP-460192