Skip to content

Commit d08770b

Browse files
authored
Merge pull request microsoft#19046 from Microsoft/fix15395
Fix emit for classes with both fields and 'extends null'
2 parents e85c633 + 264652c commit d08770b

5 files changed

Lines changed: 53 additions & 15 deletions

File tree

src/compiler/transformers/ts.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace ts {
2626
IsExportOfNamespace = 1 << 3,
2727
IsNamedExternalExport = 1 << 4,
2828
IsDefaultExternalExport = 1 << 5,
29-
HasExtendsClause = 1 << 6,
29+
IsDerivedClass = 1 << 6,
3030
UseImmediatelyInvokedFunctionExpression = 1 << 7,
3131

3232
HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators,
@@ -553,7 +553,8 @@ namespace ts {
553553
function getClassFacts(node: ClassDeclaration, staticProperties: ReadonlyArray<PropertyDeclaration>) {
554554
let facts = ClassFacts.None;
555555
if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties;
556-
if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause;
556+
const extendsClauseElement = getClassExtendsHeritageClauseElement(node);
557+
if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass;
557558
if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators;
558559
if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators;
559560
if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace;
@@ -699,7 +700,7 @@ namespace ts {
699700
name,
700701
/*typeParameters*/ undefined,
701702
visitNodes(node.heritageClauses, visitor, isHeritageClause),
702-
transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0)
703+
transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0)
703704
);
704705

705706
// To better align with the old emitter, we should not emit a trailing source map
@@ -814,7 +815,7 @@ namespace ts {
814815
// ${members}
815816
// }
816817
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
817-
const members = transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0);
818+
const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0);
818819
const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
819820
setOriginalNode(classExpression, node);
820821
setTextRange(classExpression, location);
@@ -887,11 +888,11 @@ namespace ts {
887888
* Transforms the members of a class.
888889
*
889890
* @param node The current class.
890-
* @param hasExtendsClause A value indicating whether the class has an extends clause.
891+
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
891892
*/
892-
function transformClassMembers(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) {
893+
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
893894
const members: ClassElement[] = [];
894-
const constructor = transformConstructor(node, hasExtendsClause);
895+
const constructor = transformConstructor(node, isDerivedClass);
895896
if (constructor) {
896897
members.push(constructor);
897898
}
@@ -904,9 +905,9 @@ namespace ts {
904905
* Transforms (or creates) a constructor for a class.
905906
*
906907
* @param node The current class.
907-
* @param hasExtendsClause A value indicating whether the class has an extends clause.
908+
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
908909
*/
909-
function transformConstructor(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) {
910+
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
910911
// Check if we have property assignment inside class declaration.
911912
// If there is a property assignment, we need to emit constructor whether users define it or not
912913
// If there is no property assignment, we can omit constructor if users do not define it
@@ -921,7 +922,7 @@ namespace ts {
921922
}
922923

923924
const parameters = transformConstructorParameters(constructor);
924-
const body = transformConstructorBody(node, constructor, hasExtendsClause);
925+
const body = transformConstructorBody(node, constructor, isDerivedClass);
925926

926927
// constructor(${parameters}) {
927928
// ${body}
@@ -947,7 +948,6 @@ namespace ts {
947948
* parameter property assignments or instance property initializers.
948949
*
949950
* @param constructor The constructor declaration.
950-
* @param hasExtendsClause A value indicating whether the class has an extends clause.
951951
*/
952952
function transformConstructorParameters(constructor: ConstructorDeclaration) {
953953
// The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
@@ -975,9 +975,9 @@ namespace ts {
975975
*
976976
* @param node The current class.
977977
* @param constructor The current class constructor.
978-
* @param hasExtendsClause A value indicating whether the class has an extends clause.
978+
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
979979
*/
980-
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, hasExtendsClause: boolean) {
980+
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, isDerivedClass: boolean) {
981981
let statements: Statement[] = [];
982982
let indexOfFirstStatement = 0;
983983

@@ -1001,7 +1001,7 @@ namespace ts {
10011001
const propertyAssignments = getParametersWithPropertyAssignments(constructor);
10021002
addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment));
10031003
}
1004-
else if (hasExtendsClause) {
1004+
else if (isDerivedClass) {
10051005
// Add a synthetic `super` call:
10061006
//
10071007
// super(...arguments);

tests/baselines/reference/classExtendingNull.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//// [classExtendingNull.ts]
22
class C1 extends null { }
33
class C2 extends (null) { }
4-
4+
class C3 extends null { x = 1; }
5+
class C4 extends (null) { x = 1; }
56

67
//// [classExtendingNull.js]
78
var __extends = (this && this.__extends) || (function () {
@@ -26,3 +27,17 @@ var C2 = /** @class */ (function (_super) {
2627
}
2728
return C2;
2829
}((null)));
30+
var C3 = /** @class */ (function (_super) {
31+
__extends(C3, _super);
32+
function C3() {
33+
this.x = 1;
34+
}
35+
return C3;
36+
}(null));
37+
var C4 = /** @class */ (function (_super) {
38+
__extends(C4, _super);
39+
function C4() {
40+
this.x = 1;
41+
}
42+
return C4;
43+
}((null)));

tests/baselines/reference/classExtendingNull.symbols

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,11 @@ class C1 extends null { }
55
class C2 extends (null) { }
66
>C2 : Symbol(C2, Decl(classExtendingNull.ts, 0, 25))
77

8+
class C3 extends null { x = 1; }
9+
>C3 : Symbol(C3, Decl(classExtendingNull.ts, 1, 27))
10+
>x : Symbol(C3.x, Decl(classExtendingNull.ts, 2, 23))
11+
12+
class C4 extends (null) { x = 1; }
13+
>C4 : Symbol(C4, Decl(classExtendingNull.ts, 2, 32))
14+
>x : Symbol(C4.x, Decl(classExtendingNull.ts, 3, 25))
15+

tests/baselines/reference/classExtendingNull.types

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,16 @@ class C2 extends (null) { }
88
>(null) : null
99
>null : null
1010

11+
class C3 extends null { x = 1; }
12+
>C3 : C3
13+
>null : null
14+
>x : number
15+
>1 : 1
16+
17+
class C4 extends (null) { x = 1; }
18+
>C4 : C4
19+
>(null) : null
20+
>null : null
21+
>x : number
22+
>1 : 1
23+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
class C1 extends null { }
22
class C2 extends (null) { }
3+
class C3 extends null { x = 1; }
4+
class C4 extends (null) { x = 1; }

0 commit comments

Comments
 (0)