Skip to content

Commit 223730d

Browse files
committed
Merge pull request #7559 from Microsoft/rewrite-class-as-expr
emit top level classes as class expressions when target=ES6 and module=System
2 parents b5f418f + 57d9a5a commit 223730d

9 files changed

+142
-15
lines changed

src/compiler/emitter.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -5278,9 +5278,11 @@ const _super = (function (geti, seti) {
52785278

52795279
function emitClassLikeDeclarationForES6AndHigher(node: ClassLikeDeclaration) {
52805280
let decoratedClassAlias: string;
5281-
const thisNodeIsDecorated = nodeIsDecorated(node);
5281+
const isHoistedDeclarationInSystemModule = shouldHoistDeclarationInSystemJsModule(node);
5282+
const isDecorated = nodeIsDecorated(node);
5283+
const rewriteAsClassExpression = isDecorated || isHoistedDeclarationInSystemModule;
52825284
if (node.kind === SyntaxKind.ClassDeclaration) {
5283-
if (thisNodeIsDecorated) {
5285+
if (rewriteAsClassExpression) {
52845286
// When we emit an ES6 class that has a class decorator, we must tailor the
52855287
// emit to certain specific cases.
52865288
//
@@ -5361,7 +5363,10 @@ const _super = (function (geti, seti) {
53615363
// [Example 4]
53625364
//
53635365

5364-
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
5366+
// NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System.
5367+
// Because of hoisting top level class declaration need to be emitted as class expressions.
5368+
// Double bind case is only required if node is decorated.
5369+
if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
53655370
decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default"));
53665371
decoratedClassAliases[getNodeId(node)] = decoratedClassAlias;
53675372
write(`let ${decoratedClassAlias};`);
@@ -5372,7 +5377,9 @@ const _super = (function (geti, seti) {
53725377
write("export ");
53735378
}
53745379

5375-
write("let ");
5380+
if (!isHoistedDeclarationInSystemModule) {
5381+
write("let ");
5382+
}
53765383
emitDeclarationName(node);
53775384
if (decoratedClassAlias !== undefined) {
53785385
write(` = ${decoratedClassAlias}`);
@@ -5416,7 +5423,7 @@ const _super = (function (geti, seti) {
54165423
// emit name if
54175424
// - node has a name
54185425
// - this is default export with static initializers
5419-
if (node.name || (node.flags & NodeFlags.Default && (staticProperties.length > 0 || modulekind !== ModuleKind.ES6) && !thisNodeIsDecorated)) {
5426+
if (node.name || (node.flags & NodeFlags.Default && (staticProperties.length > 0 || modulekind !== ModuleKind.ES6) && !rewriteAsClassExpression)) {
54205427
write(" ");
54215428
emitDeclarationName(node);
54225429
}
@@ -5436,7 +5443,7 @@ const _super = (function (geti, seti) {
54365443
writeLine();
54375444
emitToken(SyntaxKind.CloseBraceToken, node.members.end);
54385445

5439-
if (thisNodeIsDecorated) {
5446+
if (rewriteAsClassExpression) {
54405447
decoratedClassAliases[getNodeId(node)] = undefined;
54415448
write(";");
54425449
}
@@ -5476,7 +5483,7 @@ const _super = (function (geti, seti) {
54765483
// module), export it
54775484
if (node.flags & NodeFlags.Default) {
54785485
// if this is a top level default export of decorated class, write the export after the declaration.
5479-
if (thisNodeIsDecorated) {
5486+
if (isDecorated) {
54805487
writeLine();
54815488
write("export default ");
54825489
emitDeclarationName(node);

tests/baselines/reference/anonymousDefaultExportsSystem.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ System.register([], function(exports_1, context_1) {
1414
return {
1515
setters:[],
1616
execute: function() {
17-
class default_1 {
18-
}
17+
default_1 = class {
18+
};
1919
exports_1("default", default_1);
2020
}
2121
}

tests/baselines/reference/decoratedDefaultExportsGetExportedSystem.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ System.register([], function(exports_1, context_1) {
2626
return {
2727
setters:[],
2828
execute: function() {
29-
let Foo = class Foo {
29+
Foo = class Foo {
3030
};
3131
Foo = __decorate([
3232
decorator
@@ -49,7 +49,7 @@ System.register([], function(exports_1, context_1) {
4949
return {
5050
setters:[],
5151
execute: function() {
52-
let default_1 = class {
52+
default_1 = class {
5353
};
5454
default_1 = __decorate([
5555
decorator

tests/baselines/reference/defaultExportsGetExportedSystem.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ System.register([], function(exports_1, context_1) {
1515
return {
1616
setters:[],
1717
execute: function() {
18-
class Foo {
19-
}
18+
Foo = class Foo {
19+
};
2020
exports_1("default", Foo);
2121
}
2222
}

tests/baselines/reference/outFilerootDirModuleNamesSystem.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ System.register("a", ["b"], function(exports_2, context_2) {
3737
b_1 = b_1_1;
3838
}],
3939
execute: function() {
40-
class Foo {
41-
}
40+
Foo = class Foo {
41+
};
4242
exports_2("default", Foo);
4343
b_1.default();
4444
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [systemModuleTargetES6.ts]
2+
export class MyClass { }
3+
export class MyClass2 {
4+
static value = 42;
5+
static getInstance() { return MyClass2.value; }
6+
}
7+
8+
export function myFunction() {
9+
return new MyClass();
10+
}
11+
12+
export function myFunction2() {
13+
return new MyClass2();
14+
}
15+
16+
//// [systemModuleTargetES6.js]
17+
System.register([], function(exports_1, context_1) {
18+
"use strict";
19+
var __moduleName = context_1 && context_1.id;
20+
var MyClass, MyClass2;
21+
function myFunction() {
22+
return new MyClass();
23+
}
24+
exports_1("myFunction", myFunction);
25+
function myFunction2() {
26+
return new MyClass2();
27+
}
28+
exports_1("myFunction2", myFunction2);
29+
return {
30+
setters:[],
31+
execute: function() {
32+
MyClass = class MyClass {
33+
};
34+
exports_1("MyClass", MyClass);
35+
MyClass2 = class MyClass2 {
36+
static getInstance() { return MyClass2.value; }
37+
};
38+
MyClass2.value = 42;
39+
exports_1("MyClass2", MyClass2);
40+
}
41+
}
42+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/compiler/systemModuleTargetES6.ts ===
2+
export class MyClass { }
3+
>MyClass : Symbol(MyClass, Decl(systemModuleTargetES6.ts, 0, 0))
4+
5+
export class MyClass2 {
6+
>MyClass2 : Symbol(MyClass2, Decl(systemModuleTargetES6.ts, 0, 24))
7+
8+
static value = 42;
9+
>value : Symbol(MyClass2.value, Decl(systemModuleTargetES6.ts, 1, 23))
10+
11+
static getInstance() { return MyClass2.value; }
12+
>getInstance : Symbol(MyClass2.getInstance, Decl(systemModuleTargetES6.ts, 2, 22))
13+
>MyClass2.value : Symbol(MyClass2.value, Decl(systemModuleTargetES6.ts, 1, 23))
14+
>MyClass2 : Symbol(MyClass2, Decl(systemModuleTargetES6.ts, 0, 24))
15+
>value : Symbol(MyClass2.value, Decl(systemModuleTargetES6.ts, 1, 23))
16+
}
17+
18+
export function myFunction() {
19+
>myFunction : Symbol(myFunction, Decl(systemModuleTargetES6.ts, 4, 1))
20+
21+
return new MyClass();
22+
>MyClass : Symbol(MyClass, Decl(systemModuleTargetES6.ts, 0, 0))
23+
}
24+
25+
export function myFunction2() {
26+
>myFunction2 : Symbol(myFunction2, Decl(systemModuleTargetES6.ts, 8, 1))
27+
28+
return new MyClass2();
29+
>MyClass2 : Symbol(MyClass2, Decl(systemModuleTargetES6.ts, 0, 24))
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
=== tests/cases/compiler/systemModuleTargetES6.ts ===
2+
export class MyClass { }
3+
>MyClass : MyClass
4+
5+
export class MyClass2 {
6+
>MyClass2 : MyClass2
7+
8+
static value = 42;
9+
>value : number
10+
>42 : number
11+
12+
static getInstance() { return MyClass2.value; }
13+
>getInstance : () => number
14+
>MyClass2.value : number
15+
>MyClass2 : typeof MyClass2
16+
>value : number
17+
}
18+
19+
export function myFunction() {
20+
>myFunction : () => MyClass
21+
22+
return new MyClass();
23+
>new MyClass() : MyClass
24+
>MyClass : typeof MyClass
25+
}
26+
27+
export function myFunction2() {
28+
>myFunction2 : () => MyClass2
29+
30+
return new MyClass2();
31+
>new MyClass2() : MyClass2
32+
>MyClass2 : typeof MyClass2
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @target: ES6
2+
// @module: System
3+
export class MyClass { }
4+
export class MyClass2 {
5+
static value = 42;
6+
static getInstance() { return MyClass2.value; }
7+
}
8+
9+
export function myFunction() {
10+
return new MyClass();
11+
}
12+
13+
export function myFunction2() {
14+
return new MyClass2();
15+
}

0 commit comments

Comments
 (0)