From f61ec7fb604a897e2bb581fc8623c39d84ff71f0 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 2 May 2017 13:36:01 -0700 Subject: [PATCH] Declare synthetic var for class extends expression Classes that extend expressions will get a synthetic var declaration for the expression. This is required for classes that extend an expression that return an intersection type. --- src/compiler/checker.ts | 2 +- src/compiler/declarationEmitter.ts | 83 +++++++++++++++++++----------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 18e57303c12..dac3dce3b32 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22645,7 +22645,7 @@ namespace ts { const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(node)); resolveBaseTypesOfClass(classType); const baseType = classType.resolvedBaseTypes.length ? classType.resolvedBaseTypes[0] : unknownType; - if (!baseType.symbol) { + if (!baseType.symbol && !(baseType.flags & TypeFlags.Intersection)) { writer.reportIllegalExtends(); } getSymbolDisplayBuilder().buildTypeDisplay(baseType, writer, enclosingDeclaration, flags); diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 2bd8d5971fb..7251fec169f 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -594,12 +594,11 @@ namespace ts { emitLines(node.statements); } - // Return a temp variable name to be used in `export default` statements. + // Return a temp variable name to be used in `export default`/`export class ... extends` statements. // The temp name will be of the form _default_counter. // Note that export default is only allowed at most once in a module, so we // do not need to keep track of created temp names. - function getExportDefaultTempVariableName(): string { - const baseName = "_default"; + function getExportTempVariableName(baseName: string): string { if (!currentIdentifiers.has(baseName)) { return baseName; } @@ -613,24 +612,31 @@ namespace ts { } } + function emitTempVariableDeclaration(expr: Expression, baseName: string, diagnostic: SymbolAccessibilityDiagnostic): string { + const tempVarName = getExportTempVariableName(baseName); + if (!noDeclare) { + write("declare "); + } + write("var "); + write(tempVarName); + write(": "); + writer.getSymbolAccessibilityDiagnostic = () => diagnostic; + resolver.writeTypeOfExpression(expr, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer); + write(";"); + writeLine(); + return tempVarName; + } + function emitExportAssignment(node: ExportAssignment) { if (node.expression.kind === SyntaxKind.Identifier) { write(node.isExportEquals ? "export = " : "export default "); writeTextOfNode(currentText, node.expression); } else { - // Expression - const tempVarName = getExportDefaultTempVariableName(); - if (!noDeclare) { - write("declare "); - } - write("var "); - write(tempVarName); - write(": "); - writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic; - resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer); - write(";"); - writeLine(); + const tempVarName = emitTempVariableDeclaration(node.expression, "_default", { + diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, + errorNode: node + }); write(node.isExportEquals ? "export = " : "export default "); write(tempVarName); } @@ -644,13 +650,6 @@ namespace ts { // write each of these declarations asynchronously writeAsynchronousModuleElements(nodes); } - - function getDefaultExportAccessibilityDiagnostic(): SymbolAccessibilityDiagnostic { - return { - diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0, - errorNode: node - }; - } } function isModuleElementVisible(node: Declaration) { @@ -1113,7 +1112,11 @@ namespace ts { else { writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError; errorNameNode = className; - resolver.writeBaseConstructorTypeOfClass(enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer); + resolver.writeBaseConstructorTypeOfClass( + enclosingDeclaration as ClassLikeDeclaration, + enclosingDeclaration, + TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, + writer); errorNameNode = undefined; } @@ -1151,21 +1154,39 @@ namespace ts { } } + const prevEnclosingDeclaration = enclosingDeclaration; + enclosingDeclaration = node; + const baseTypeNode = getClassExtendsHeritageClauseElement(node); + let tempVarName: string; + if (isNonNullExpression(baseTypeNode)) { + tempVarName = emitTempVariableDeclaration(baseTypeNode.expression, `_${node.name.text}_intersection_base`, { + diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1, + errorNode: baseTypeNode, + typeName: node.name + }); + } + emitJsDocComments(node); emitModuleElementDeclarationFlags(node); if (hasModifier(node, ModifierFlags.Abstract)) { write("abstract "); } - write("class "); writeTextOfNode(currentText, node.name); - const prevEnclosingDeclaration = enclosingDeclaration; - enclosingDeclaration = node; emitTypeParameters(node.typeParameters); - const baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { - node.name; - emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false); + if (isNonNullExpression(baseTypeNode)) { + write(" extends "); + write(tempVarName); + if (baseTypeNode.typeArguments) { + write("<"); + emitCommaList(baseTypeNode.typeArguments, emitType); + write(">"); + } + } + else { + emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false); + } } emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true); write(" {"); @@ -1201,6 +1222,10 @@ namespace ts { enclosingDeclaration = prevEnclosingDeclaration; } + function isNonNullExpression(node: ExpressionWithTypeArguments) { + return node && !isEntityNameExpression(node.expression) && node.expression.kind !== SyntaxKind.NullKeyword; + } + function emitPropertyDeclaration(node: Declaration) { if (hasDynamicName(node)) { return;