Open joeywatts opened 6 years ago
There's a type error on the line with the computed property:
A computed property name in a class property declaration must refer to an expression whose type is a literal type or a 'unique symbol' type.
However
Taking PRs and marking it as moderate (assuming you are familiar with spec-reading and the transform pipeline).
What are the implications of getting rid of the type error? Will allowing arbitrary string and symbol values in computed field declarations affect inference?
Type errors aside, here's one possible emit that works correctly. I haven't seen any other cases of invoking an IIFE with parameters like this. Are there any?
var classes = [];
for (var i = 0; i <= 10; ++i) {
classes.push(
function(i) {
function A() {
this[i] = "my property";
}
return A;
}(i)
);
}
Maybe we can lean on the iife stuff that's already done to transpile let
and const
.
The currently generated code for Joey's example when targeting ES2015 is:
var _a, _b;
"use strict";
const classes = [];
for (let i = 0; i <= 10; ++i) {
classes.push((_b = class A {
constructor() {
this[_a] = "my property";
}
},
_a = i,
_b));
}
for (const clazz of classes) {
console.log(Object.getOwnPropertyNames(new clazz()));
}
If we move _a
and _b
into the containing scope of the class and use let
then the output is correct:
"use strict";
const classes = [];
for (let i = 0; i <= 10; ++i) {
let _a, _b; // <---- was moved
classes.push((_b = class A {
constructor() {
this[_a] = "my property";
}
},
_a = i,
_b));
}
for (const clazz of classes) {
console.log(Object.getOwnPropertyNames(new clazz()));
}
Update: would this be a big architectural change? If I understand correctly,
lexicalEnvironmentStack
is used in hoisting declarations, and is defined in transformer.ts, rather than in a particular transformer.
the special temporary variable and hoist is happened in ts transformer before es2015 perhaps we need a block level hoist?
@Kingwl agreed. we're trying that approach in https://github.com/joeywatts/TypeScript/pull/15
used that approach in https://github.com/Microsoft/TypeScript/pull/28708
I noticed that there's also an issue with the binding of property declaration initializers in class expressions which are within an iteration statement. For example:
let arr = [];
for (let i = 0; i < 5; ++i) {
arr.push(class C {
test = i;
})
}
arr.forEach(clazz => console.log(new clazz().test));
Since the i
is block scoped within the loop, each class should capture its own value of i
(causing it to print 0, 1, 2, 3, 4). The TypeScript compiler currently does not preserve this binding when targeting ES5 or lower. This currently produces no compile error and leads to different run time behavior when targeting ES2015 vs ES5.
I've investigated the changes required to the checker in order to pick up this binding, and it appears to me that a simple change to the checkNestedScopeBinding
function to account for usage sites within instance property initializers (in addition to functions) would work. Should I open a separate issue for this?
I think code like this should be supported:
class A<P extends PropertyKey> {
[p in P]: number;
}
But now it only support:
const m = Symbol('m');
const n = 'n' as 'n';
class A {
[m]: number;
[n]: string;
}
TypeScript Version: 3.2.0-dev.20181011
Search Terms: computed property expression
Code
Expected behavior: The log statements indicate that each class in
classes
has a different property name (i
should be evaluated at the time of the class evaluation and all instances of that class should have a property name corresponding to that evaluation ofi
).Actual behavior: Compiled code logs:
Playground Link)%3B%0D%0A%7D)