diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 4feb3ca0c26..e67918fd696 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -26,7 +26,7 @@ namespace ts { IsExportOfNamespace = 1 << 3, IsNamedExternalExport = 1 << 4, IsDefaultExternalExport = 1 << 5, - HasExtendsClause = 1 << 6, + IsDerivedClass = 1 << 6, UseImmediatelyInvokedFunctionExpression = 1 << 7, HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators, @@ -553,7 +553,8 @@ namespace ts { function getClassFacts(node: ClassDeclaration, staticProperties: ReadonlyArray) { let facts = ClassFacts.None; if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties; - if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause; + const extendsClauseElement = getClassExtendsHeritageClauseElement(node); + if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass; if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators; if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators; if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace; @@ -699,7 +700,7 @@ namespace ts { name, /*typeParameters*/ undefined, visitNodes(node.heritageClauses, visitor, isHeritageClause), - transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0) + transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0) ); // To better align with the old emitter, we should not emit a trailing source map @@ -814,7 +815,7 @@ namespace ts { // ${members} // } const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); - const members = transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0); + const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0); const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members); setOriginalNode(classExpression, node); setTextRange(classExpression, location); @@ -887,11 +888,11 @@ namespace ts { * Transforms the members of a class. * * @param node The current class. - * @param hasExtendsClause A value indicating whether the class has an extends clause. + * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. */ - function transformClassMembers(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) { + function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { const members: ClassElement[] = []; - const constructor = transformConstructor(node, hasExtendsClause); + const constructor = transformConstructor(node, isDerivedClass); if (constructor) { members.push(constructor); } @@ -904,9 +905,9 @@ namespace ts { * Transforms (or creates) a constructor for a class. * * @param node The current class. - * @param hasExtendsClause A value indicating whether the class has an extends clause. + * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. */ - function transformConstructor(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) { + function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { // Check if we have property assignment inside class declaration. // If there is a property assignment, we need to emit constructor whether users define it or not // If there is no property assignment, we can omit constructor if users do not define it @@ -921,7 +922,7 @@ namespace ts { } const parameters = transformConstructorParameters(constructor); - const body = transformConstructorBody(node, constructor, hasExtendsClause); + const body = transformConstructorBody(node, constructor, isDerivedClass); // constructor(${parameters}) { // ${body} @@ -947,7 +948,6 @@ namespace ts { * parameter property assignments or instance property initializers. * * @param constructor The constructor declaration. - * @param hasExtendsClause A value indicating whether the class has an extends clause. */ function transformConstructorParameters(constructor: ConstructorDeclaration) { // The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation: @@ -975,9 +975,9 @@ namespace ts { * * @param node The current class. * @param constructor The current class constructor. - * @param hasExtendsClause A value indicating whether the class has an extends clause. + * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. */ - function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, hasExtendsClause: boolean) { + function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, isDerivedClass: boolean) { let statements: Statement[] = []; let indexOfFirstStatement = 0; @@ -1001,7 +1001,7 @@ namespace ts { const propertyAssignments = getParametersWithPropertyAssignments(constructor); addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); } - else if (hasExtendsClause) { + else if (isDerivedClass) { // Add a synthetic `super` call: // // super(...arguments); diff --git a/tests/baselines/reference/classExtendingNull.js b/tests/baselines/reference/classExtendingNull.js index 6c6ae8a0167..f405f6da84c 100644 --- a/tests/baselines/reference/classExtendingNull.js +++ b/tests/baselines/reference/classExtendingNull.js @@ -1,7 +1,8 @@ //// [classExtendingNull.ts] class C1 extends null { } class C2 extends (null) { } - +class C3 extends null { x = 1; } +class C4 extends (null) { x = 1; } //// [classExtendingNull.js] var __extends = (this && this.__extends) || (function () { @@ -26,3 +27,17 @@ var C2 = /** @class */ (function (_super) { } return C2; }((null))); +var C3 = /** @class */ (function (_super) { + __extends(C3, _super); + function C3() { + this.x = 1; + } + return C3; +}(null)); +var C4 = /** @class */ (function (_super) { + __extends(C4, _super); + function C4() { + this.x = 1; + } + return C4; +}((null))); diff --git a/tests/baselines/reference/classExtendingNull.symbols b/tests/baselines/reference/classExtendingNull.symbols index 37a6162f414..eff1f18c0ca 100644 --- a/tests/baselines/reference/classExtendingNull.symbols +++ b/tests/baselines/reference/classExtendingNull.symbols @@ -5,3 +5,11 @@ class C1 extends null { } class C2 extends (null) { } >C2 : Symbol(C2, Decl(classExtendingNull.ts, 0, 25)) +class C3 extends null { x = 1; } +>C3 : Symbol(C3, Decl(classExtendingNull.ts, 1, 27)) +>x : Symbol(C3.x, Decl(classExtendingNull.ts, 2, 23)) + +class C4 extends (null) { x = 1; } +>C4 : Symbol(C4, Decl(classExtendingNull.ts, 2, 32)) +>x : Symbol(C4.x, Decl(classExtendingNull.ts, 3, 25)) + diff --git a/tests/baselines/reference/classExtendingNull.types b/tests/baselines/reference/classExtendingNull.types index 3c572a3406c..e98f8daba06 100644 --- a/tests/baselines/reference/classExtendingNull.types +++ b/tests/baselines/reference/classExtendingNull.types @@ -8,3 +8,16 @@ class C2 extends (null) { } >(null) : null >null : null +class C3 extends null { x = 1; } +>C3 : C3 +>null : null +>x : number +>1 : 1 + +class C4 extends (null) { x = 1; } +>C4 : C4 +>(null) : null +>null : null +>x : number +>1 : 1 + diff --git a/tests/cases/conformance/classes/classDeclarations/classExtendingNull.ts b/tests/cases/conformance/classes/classDeclarations/classExtendingNull.ts index 655cf44ed57..b00c047a379 100644 --- a/tests/cases/conformance/classes/classDeclarations/classExtendingNull.ts +++ b/tests/cases/conformance/classes/classDeclarations/classExtendingNull.ts @@ -1,2 +1,4 @@ class C1 extends null { } class C2 extends (null) { } +class C3 extends null { x = 1; } +class C4 extends (null) { x = 1; } \ No newline at end of file