Skip to content

Commit c556749

Browse files
committed
Merge pull request #3568 from Microsoft/classExpressions
Class expressions
2 parents 30657d4 + 55f195d commit c556749

File tree

61 files changed

+696
-273
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+696
-273
lines changed

src/compiler/binder.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ namespace ts {
630630
function getStrictModeIdentifierMessage(node: Node) {
631631
// Provide specialized messages to help the user understand why we think they're in
632632
// strict mode.
633-
if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) {
633+
if (getContainingClass(node)) {
634634
return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode;
635635
}
636636

@@ -688,7 +688,7 @@ namespace ts {
688688
function getStrictModeEvalOrArgumentsMessage(node: Node) {
689689
// Provide specialized messages to help the user understand why we think they're in
690690
// strict mode.
691-
if (getAncestor(node, SyntaxKind.ClassDeclaration) || getAncestor(node, SyntaxKind.ClassExpression)) {
691+
if (getContainingClass(node)) {
692692
return Diagnostics.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode;
693693
}
694694

@@ -1031,7 +1031,7 @@ namespace ts {
10311031
// containing class.
10321032
if (node.flags & NodeFlags.AccessibilityModifier &&
10331033
node.parent.kind === SyntaxKind.Constructor &&
1034-
(node.parent.parent.kind === SyntaxKind.ClassDeclaration || node.parent.parent.kind === SyntaxKind.ClassExpression)) {
1034+
isClassLike(node.parent.parent)) {
10351035

10361036
let classDeclaration = <ClassLikeDeclaration>node.parent.parent;
10371037
declareSymbol(classDeclaration.symbol.members, classDeclaration.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);

src/compiler/checker.ts

+79-97
Large diffs are not rendered by default.

src/compiler/diagnosticInformationMap.generated.ts

-2
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,5 @@ namespace ts {
567567
enum_declarations_can_only_be_used_in_a_ts_file: { code: 8015, category: DiagnosticCategory.Error, key: "'enum declarations' can only be used in a .ts file." },
568568
type_assertion_expressions_can_only_be_used_in_a_ts_file: { code: 8016, category: DiagnosticCategory.Error, key: "'type assertion expressions' can only be used in a .ts file." },
569569
decorators_can_only_be_used_in_a_ts_file: { code: 8017, category: DiagnosticCategory.Error, key: "'decorators' can only be used in a .ts file." },
570-
Only_identifiers_Slashqualified_names_with_optional_type_arguments_are_currently_supported_in_a_class_extends_clauses: { code: 9002, category: DiagnosticCategory.Error, key: "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clauses." },
571-
class_expressions_are_not_currently_supported: { code: 9003, category: DiagnosticCategory.Error, key: "'class' expressions are not currently supported." },
572570
};
573571
}

src/compiler/diagnosticMessages.json

-9
Original file line numberDiff line numberDiff line change
@@ -2259,14 +2259,5 @@
22592259
"'decorators' can only be used in a .ts file.": {
22602260
"category": "Error",
22612261
"code": 8017
2262-
},
2263-
2264-
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clauses.": {
2265-
"category": "Error",
2266-
"code": 9002
2267-
},
2268-
"'class' expressions are not currently supported.": {
2269-
"category": "Error",
2270-
"code": 9003
22712262
}
22722263
}

src/compiler/emitter.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
264264
return makeUniqueName("default");
265265
}
266266

267+
function generateNameForClassExpression() {
268+
return makeUniqueName("class");
269+
}
270+
267271
function generateNameForNode(node: Node) {
268272
switch (node.kind) {
269273
case SyntaxKind.Identifier:
@@ -276,9 +280,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
276280
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>node);
277281
case SyntaxKind.FunctionDeclaration:
278282
case SyntaxKind.ClassDeclaration:
279-
case SyntaxKind.ClassExpression:
280283
case SyntaxKind.ExportAssignment:
281284
return generateNameForExportDefault();
285+
case SyntaxKind.ClassExpression:
286+
return generateNameForClassExpression();
282287
}
283288
}
284289

src/compiler/utilities.ts

+17-18
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ namespace ts {
542542
case SyntaxKind.ModuleDeclaration:
543543
case SyntaxKind.TypeAliasDeclaration:
544544
case SyntaxKind.ClassDeclaration:
545+
case SyntaxKind.ClassExpression:
545546
// These are not allowed inside a generator now, but eventually they may be allowed
546547
// as local types. Regardless, any yield statements contained within them should be
547548
// skipped in this traversal.
@@ -579,26 +580,15 @@ namespace ts {
579580
return true;
580581
}
581582
}
582-
583583
return false;
584584
}
585585

586586
export function isAccessor(node: Node): boolean {
587-
if (node) {
588-
switch (node.kind) {
589-
case SyntaxKind.GetAccessor:
590-
case SyntaxKind.SetAccessor:
591-
return true;
592-
}
593-
}
594-
595-
return false;
587+
return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor);
596588
}
597589

598590
export function isClassLike(node: Node): boolean {
599-
if (node) {
600-
return node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression;
601-
}
591+
return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression);
602592
}
603593

604594
export function isFunctionLike(node: Node): boolean {
@@ -620,7 +610,6 @@ namespace ts {
620610
return true;
621611
}
622612
}
623-
624613
return false;
625614
}
626615

@@ -641,6 +630,15 @@ namespace ts {
641630
}
642631
}
643632

633+
export function getContainingClass(node: Node): ClassLikeDeclaration {
634+
while (true) {
635+
node = node.parent;
636+
if (!node || isClassLike(node)) {
637+
return <ClassLikeDeclaration>node;
638+
}
639+
}
640+
}
641+
644642
export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node {
645643
while (true) {
646644
node = node.parent;
@@ -653,7 +651,7 @@ namespace ts {
653651
// then the computed property is not a 'this' container.
654652
// A computed property name in a class needs to be a this container
655653
// so that we can error on it.
656-
if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
654+
if (isClassLike(node.parent.parent)) {
657655
return node;
658656
}
659657
// If this is a computed property, then the parent should not
@@ -708,7 +706,7 @@ namespace ts {
708706
// then the computed property is not a 'super' container.
709707
// A computed property name in a class needs to be a super container
710708
// so that we can error on it.
711-
if (node.parent.parent.kind === SyntaxKind.ClassDeclaration) {
709+
if (isClassLike(node.parent.parent)) {
712710
return node;
713711
}
714712
// If this is a computed property, then the parent should not
@@ -1087,6 +1085,7 @@ namespace ts {
10871085
case SyntaxKind.ArrowFunction:
10881086
case SyntaxKind.BindingElement:
10891087
case SyntaxKind.ClassDeclaration:
1088+
case SyntaxKind.ClassExpression:
10901089
case SyntaxKind.Constructor:
10911090
case SyntaxKind.EnumDeclaration:
10921091
case SyntaxKind.EnumMember:
@@ -1235,7 +1234,7 @@ namespace ts {
12351234
return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
12361235
}
12371236

1238-
export function getClassImplementsHeritageClauseElements(node: ClassDeclaration) {
1237+
export function getClassImplementsHeritageClauseElements(node: ClassLikeDeclaration) {
12391238
let heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword);
12401239
return heritageClause ? heritageClause.types : undefined;
12411240
}
@@ -1917,7 +1916,7 @@ namespace ts {
19171916
export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean {
19181917
return node.kind === SyntaxKind.ExpressionWithTypeArguments &&
19191918
(<HeritageClause>node.parent).token === SyntaxKind.ExtendsKeyword &&
1920-
node.parent.parent.kind === SyntaxKind.ClassDeclaration;
1919+
isClassLike(node.parent.parent);
19211920
}
19221921

19231922
// Returns false if this heritage clause element's expression contains something unsupported

tests/baselines/reference/anonymousClassExpression1.errors.txt

-9
This file was deleted.

tests/baselines/reference/anonymousClassExpression1.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ function f() {
66
//// [anonymousClassExpression1.js]
77
function f() {
88
return typeof (function () {
9-
function default_1() {
9+
function class_1() {
1010
}
11-
return default_1;
11+
return class_1;
1212
})() === "function";
1313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=== tests/cases/compiler/anonymousClassExpression1.ts ===
2+
function f() {
3+
>f : Symbol(f, Decl(anonymousClassExpression1.ts, 0, 0))
4+
5+
return typeof class {} === "function";
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/compiler/anonymousClassExpression1.ts ===
2+
function f() {
3+
>f : () => boolean
4+
5+
return typeof class {} === "function";
6+
>typeof class {} === "function" : boolean
7+
>typeof class {} : string
8+
>class {} : typeof (Anonymous class)
9+
>"function" : string
10+
}

tests/baselines/reference/classExpression.errors.txt

-24
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/conformance/classes/classExpression.ts ===
2+
var x = class C {
3+
>x : Symbol(x, Decl(classExpression.ts, 0, 3))
4+
>C : Symbol(C, Decl(classExpression.ts, 0, 7))
5+
}
6+
7+
var y = {
8+
>y : Symbol(y, Decl(classExpression.ts, 3, 3))
9+
10+
foo: class C2 {
11+
>foo : Symbol(foo, Decl(classExpression.ts, 3, 9))
12+
>C2 : Symbol(C2, Decl(classExpression.ts, 4, 8))
13+
}
14+
}
15+
16+
module M {
17+
>M : Symbol(M, Decl(classExpression.ts, 6, 1))
18+
19+
var z = class C4 {
20+
>z : Symbol(z, Decl(classExpression.ts, 9, 7))
21+
>C4 : Symbol(C4, Decl(classExpression.ts, 9, 11))
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
=== tests/cases/conformance/classes/classExpression.ts ===
2+
var x = class C {
3+
>x : typeof C
4+
>class C {} : typeof C
5+
>C : typeof C
6+
}
7+
8+
var y = {
9+
>y : { foo: typeof C2; }
10+
>{ foo: class C2 { }} : { foo: typeof C2; }
11+
12+
foo: class C2 {
13+
>foo : typeof C2
14+
>class C2 { } : typeof C2
15+
>C2 : typeof C2
16+
}
17+
}
18+
19+
module M {
20+
>M : typeof M
21+
22+
var z = class C4 {
23+
>z : typeof C4
24+
>class C4 { } : typeof C4
25+
>C4 : typeof C4
26+
}
27+
}

tests/baselines/reference/classExpression1.errors.txt

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpression1.ts ===
2+
var v = class C {};
3+
>v : Symbol(v, Decl(classExpression1.ts, 0, 3))
4+
>C : Symbol(C, Decl(classExpression1.ts, 0, 7))
5+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpression1.ts ===
2+
var v = class C {};
3+
>v : typeof C
4+
>class C {} : typeof C
5+
>C : typeof C
6+

tests/baselines/reference/classExpression2.errors.txt

-8
This file was deleted.

tests/baselines/reference/classExpression2.js

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ class D { }
33
var v = class C extends D {};
44

55
//// [classExpression2.js]
6+
var __extends = (this && this.__extends) || function (d, b) {
7+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
8+
function __() { this.constructor = d; }
9+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
10+
};
611
var D = (function () {
712
function D() {
813
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpression2.ts ===
2+
class D { }
3+
>D : Symbol(D, Decl(classExpression2.ts, 0, 0))
4+
5+
var v = class C extends D {};
6+
>v : Symbol(v, Decl(classExpression2.ts, 1, 3))
7+
>C : Symbol(C, Decl(classExpression2.ts, 1, 7))
8+
>D : Symbol(D, Decl(classExpression2.ts, 0, 0))
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/classes/classExpressions/classExpression2.ts ===
2+
class D { }
3+
>D : D
4+
5+
var v = class C extends D {};
6+
>v : typeof C
7+
>class C extends D {} : typeof C
8+
>C : typeof C
9+
>D : D
10+

0 commit comments

Comments
 (0)