Skip to content

Commit d56f2f6

Browse files
Merge pull request #2603 from Microsoft/staticClassProps
Add support for emitting static properties in class expressions in ES6.
2 parents 65cbd91 + 8537232 commit d56f2f6

16 files changed

+204
-30
lines changed

src/compiler/emitter.ts

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,28 +3199,49 @@ module ts {
31993199
}
32003200
}
32013201

3202-
function emitMemberAssignments(node: ClassLikeDeclaration, staticFlag: NodeFlags) {
3203-
forEach(node.members, member => {
3204-
if (member.kind === SyntaxKind.PropertyDeclaration && (member.flags & NodeFlags.Static) === staticFlag && (<PropertyDeclaration>member).initializer) {
3205-
writeLine();
3206-
emitLeadingComments(member);
3207-
emitStart(member);
3208-
emitStart((<PropertyDeclaration>member).name);
3209-
if (staticFlag) {
3210-
emitDeclarationName(node);
3211-
}
3212-
else {
3213-
write("this");
3214-
}
3215-
emitMemberAccessForPropertyName((<PropertyDeclaration>member).name);
3216-
emitEnd((<PropertyDeclaration>member).name);
3217-
write(" = ");
3218-
emit((<PropertyDeclaration>member).initializer);
3219-
write(";");
3220-
emitEnd(member);
3221-
emitTrailingComments(member);
3202+
function getInitializedProperties(node: ClassLikeDeclaration, static: boolean) {
3203+
let properties: PropertyDeclaration[] = [];
3204+
for (let member of node.members) {
3205+
if (member.kind === SyntaxKind.PropertyDeclaration && static === ((member.flags & NodeFlags.Static) !== 0) && (<PropertyDeclaration>member).initializer) {
3206+
properties.push(<PropertyDeclaration>member);
32223207
}
3223-
});
3208+
}
3209+
3210+
return properties;
3211+
}
3212+
3213+
function emitPropertyDeclarations(node: ClassLikeDeclaration, properties: PropertyDeclaration[]) {
3214+
for (let property of properties) {
3215+
emitPropertyDeclaration(node, property);
3216+
}
3217+
}
3218+
3219+
function emitPropertyDeclaration(node: ClassLikeDeclaration, property: PropertyDeclaration, receiver?: Identifier, isExpression?: boolean) {
3220+
writeLine();
3221+
emitLeadingComments(property);
3222+
emitStart(property);
3223+
emitStart(property.name);
3224+
if (receiver) {
3225+
emit(receiver);
3226+
}
3227+
else {
3228+
if (property.flags & NodeFlags.Static) {
3229+
emitDeclarationName(node);
3230+
}
3231+
else {
3232+
write("this");
3233+
}
3234+
}
3235+
emitMemberAccessForPropertyName(property.name);
3236+
emitEnd(property.name);
3237+
write(" = ");
3238+
emit(property.initializer);
3239+
if (!isExpression) {
3240+
write(";");
3241+
}
3242+
3243+
emitEnd(property);
3244+
emitTrailingComments(property);
32243245
}
32253246

32263247
function emitMemberFunctionsForES5AndLower(node: ClassLikeDeclaration) {
@@ -3338,6 +3359,14 @@ module ts {
33383359
tempVariables = undefined;
33393360
tempParameters = undefined;
33403361

3362+
emitConstructorWorker(node, baseTypeElement);
3363+
3364+
tempFlags = saveTempFlags;
3365+
tempVariables = saveTempVariables;
3366+
tempParameters = saveTempParameters;
3367+
}
3368+
3369+
function emitConstructorWorker(node: ClassLikeDeclaration, baseTypeElement: HeritageClauseElement) {
33413370
// Check if we have property assignment inside class declaration.
33423371
// If there is property assignment, we need to emit constructor whether users define it or not
33433372
// If there is no property assignment, we can omit constructor if users do not define it
@@ -3425,7 +3454,7 @@ module ts {
34253454
emitEnd(baseTypeElement);
34263455
}
34273456
}
3428-
emitMemberAssignments(node, /*staticFlag*/0);
3457+
emitPropertyDeclarations(node, getInitializedProperties(node, /*static:*/ false));
34293458
if (ctor) {
34303459
var statements: Node[] = (<Block>ctor.body).statements;
34313460
if (superCall) {
@@ -3445,10 +3474,6 @@ module ts {
34453474
if (ctor) {
34463475
emitTrailingComments(ctor);
34473476
}
3448-
3449-
tempFlags = saveTempFlags;
3450-
tempVariables = saveTempVariables;
3451-
tempParameters = saveTempParameters;
34523477
}
34533478

34543479
function emitClassExpression(node: ClassExpression) {
@@ -3540,6 +3565,29 @@ module ts {
35403565
}
35413566
}
35423567

3568+
// If the class has static properties, and it's a class expression, then we'll need
3569+
// to specialize the emit a bit. for a class expression of the form:
3570+
//
3571+
// class C { static a = 1; static b = 2; ... }
3572+
//
3573+
// We'll emit:
3574+
//
3575+
// (_temp = class C { ... }, _temp.a = 1, _temp.b = 2, _temp)
3576+
//
3577+
// This keeps the expression as an expression, while ensuring that the static parts
3578+
// of it have been initialized by the time it is used.
3579+
let staticProperties = getInitializedProperties(node, /*static:*/ true);
3580+
let isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression;
3581+
let tempVariable: Identifier;
3582+
3583+
if (isClassExpressionWithStaticProperties) {
3584+
tempVariable = createAndRecordTempVariable(TempFlags.Auto);
3585+
write("(");
3586+
increaseIndent();
3587+
emit(tempVariable);
3588+
write(" = ")
3589+
}
3590+
35433591
write("class");
35443592

35453593
// check if this is an "export default class" as it may not have a name. Do not emit the name if the class is decorated.
@@ -3590,9 +3638,24 @@ module ts {
35903638
// From ES6 specification:
35913639
// HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using
35923640
// a lexical declaration such as a LexicalDeclaration or a ClassDeclaration.
3593-
writeLine();
3594-
emitMemberAssignments(node, NodeFlags.Static);
3595-
emitDecoratorsOfClass(node);
3641+
3642+
if (isClassExpressionWithStaticProperties) {
3643+
for (var property of staticProperties) {
3644+
write(",");
3645+
writeLine();
3646+
emitPropertyDeclaration(node, property, /*receiver:*/ tempVariable, /*isExpression:*/ true);
3647+
}
3648+
write(",");
3649+
writeLine();
3650+
emit(tempVariable);
3651+
decreaseIndent();
3652+
write(")");
3653+
}
3654+
else {
3655+
writeLine();
3656+
emitPropertyDeclarations(node, staticProperties);
3657+
emitDecoratorsOfClass(node);
3658+
}
35963659

35973660
// If this is an exported class, but not on the top level (i.e. on an internal
35983661
// module), export it
@@ -3648,7 +3711,7 @@ module ts {
36483711
writeLine();
36493712
emitConstructor(node, baseTypeNode);
36503713
emitMemberFunctionsForES5AndLower(node);
3651-
emitMemberAssignments(node, NodeFlags.Static);
3714+
emitPropertyDeclarations(node, getInitializedProperties(node, /*static:*/ true));
36523715
writeLine();
36533716
emitDecoratorsOfClass(node);
36543717
writeLine();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
tests/cases/compiler/classExpressionWithDecorator1.ts(1,9): error TS1109: Expression expected.
2+
tests/cases/compiler/classExpressionWithDecorator1.ts(1,10): error TS2304: Cannot find name 'decorate'.
3+
4+
5+
==== tests/cases/compiler/classExpressionWithDecorator1.ts (2 errors) ====
6+
var v = @decorate class C { static p = 1 };
7+
~
8+
!!! error TS1109: Expression expected.
9+
~~~~~~~~
10+
!!! error TS2304: Cannot find name 'decorate'.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [classExpressionWithDecorator1.ts]
2+
var v = @decorate class C { static p = 1 };
3+
4+
//// [classExpressionWithDecorator1.js]
5+
var __decorate = this.__decorate || function (decorators, target, key, value) {
6+
var kind = typeof (arguments.length == 2 ? value = target : value);
7+
for (var i = decorators.length - 1; i >= 0; --i) {
8+
var decorator = decorators[i];
9+
switch (kind) {
10+
case "function": value = decorator(value) || value; break;
11+
case "number": decorator(target, key, value); break;
12+
case "undefined": decorator(target, key); break;
13+
case "object": value = decorator(target, key, value) || value; break;
14+
}
15+
}
16+
return value;
17+
};
18+
var v = ;
19+
var C = (function () {
20+
function C() {
21+
}
22+
C.p = 1;
23+
C = __decorate([decorate], C);
24+
return C;
25+
})();
26+
;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
tests/cases/compiler/classExpressionWithStaticProperties1.ts(1,15): error TS9003: 'class' expressions are not currently supported.
2+
3+
4+
==== tests/cases/compiler/classExpressionWithStaticProperties1.ts (1 errors) ====
5+
var v = class C { static a = 1; static b = 2 };
6+
~
7+
!!! error TS9003: 'class' expressions are not currently supported.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [classExpressionWithStaticProperties1.ts]
2+
var v = class C { static a = 1; static b = 2 };
3+
4+
//// [classExpressionWithStaticProperties1.js]
5+
var v = (function () {
6+
function C() {
7+
}
8+
C.a = 1;
9+
C.b = 2;
10+
return C;
11+
})();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
tests/cases/compiler/classExpressionWithStaticProperties2.ts(1,15): error TS9003: 'class' expressions are not currently supported.
2+
3+
4+
==== tests/cases/compiler/classExpressionWithStaticProperties2.ts (1 errors) ====
5+
var v = class C { static a = 1; static b };
6+
~
7+
!!! error TS9003: 'class' expressions are not currently supported.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [classExpressionWithStaticProperties2.ts]
2+
var v = class C { static a = 1; static b };
3+
4+
//// [classExpressionWithStaticProperties2.js]
5+
var v = (function () {
6+
function C() {
7+
}
8+
C.a = 1;
9+
return C;
10+
})();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
tests/cases/compiler/classExpressionWithStaticPropertiesES61.ts(1,15): error TS9003: 'class' expressions are not currently supported.
2+
3+
4+
==== tests/cases/compiler/classExpressionWithStaticPropertiesES61.ts (1 errors) ====
5+
var v = class C { static a = 1; static b = 2 };
6+
~
7+
!!! error TS9003: 'class' expressions are not currently supported.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [classExpressionWithStaticPropertiesES61.ts]
2+
var v = class C { static a = 1; static b = 2 };
3+
4+
//// [classExpressionWithStaticPropertiesES61.js]
5+
var v = (_a = class C {
6+
},
7+
_a.a = 1,
8+
_a.b = 2,
9+
_a);
10+
var _a;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
tests/cases/compiler/classExpressionWithStaticPropertiesES62.ts(1,15): error TS9003: 'class' expressions are not currently supported.
2+
3+
4+
==== tests/cases/compiler/classExpressionWithStaticPropertiesES62.ts (1 errors) ====
5+
var v = class C { static a = 1; static b };
6+
~
7+
!!! error TS9003: 'class' expressions are not currently supported.

0 commit comments

Comments
 (0)