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.
This commit is contained in:
Nathan Shively-Sanders 2017-05-02 13:36:01 -07:00
parent 88f9a978dd
commit f61ec7fb60
2 changed files with 55 additions and 30 deletions

View File

@ -22645,7 +22645,7 @@ namespace ts {
const classType = <InterfaceType>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);

View File

@ -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(<ClassLikeDeclaration>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;