Skip to content

Commit a1c92eb

Browse files
committed
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.
1 parent 601aa8e commit a1c92eb

File tree

2 files changed

+55
-30
lines changed

2 files changed

+55
-30
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22646,7 +22646,7 @@ namespace ts {
2264622646
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node));
2264722647
resolveBaseTypesOfClass(classType);
2264822648
const baseType = classType.resolvedBaseTypes.length ? classType.resolvedBaseTypes[0] : unknownType;
22649-
if (!baseType.symbol) {
22649+
if (!baseType.symbol && !(baseType.flags & TypeFlags.Intersection)) {
2265022650
writer.reportIllegalExtends();
2265122651
}
2265222652
getSymbolDisplayBuilder().buildTypeDisplay(baseType, writer, enclosingDeclaration, flags);

src/compiler/declarationEmitter.ts

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -594,12 +594,11 @@ namespace ts {
594594
emitLines(node.statements);
595595
}
596596

597-
// Return a temp variable name to be used in `export default` statements.
597+
// Return a temp variable name to be used in `export default`/`export class ... extends` statements.
598598
// The temp name will be of the form _default_counter.
599599
// Note that export default is only allowed at most once in a module, so we
600600
// do not need to keep track of created temp names.
601-
function getExportDefaultTempVariableName(): string {
602-
const baseName = "_default";
601+
function getExportTempVariableName(baseName: string): string {
603602
if (!currentIdentifiers.has(baseName)) {
604603
return baseName;
605604
}
@@ -613,24 +612,31 @@ namespace ts {
613612
}
614613
}
615614

615+
function emitTempVariableDeclaration(expr: Expression, baseName: string, diagnostic: SymbolAccessibilityDiagnostic): string {
616+
const tempVarName = getExportTempVariableName(baseName);
617+
if (!noDeclare) {
618+
write("declare ");
619+
}
620+
write("var ");
621+
write(tempVarName);
622+
write(": ");
623+
writer.getSymbolAccessibilityDiagnostic = () => diagnostic;
624+
resolver.writeTypeOfExpression(expr, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
625+
write(";");
626+
writeLine();
627+
return tempVarName;
628+
}
629+
616630
function emitExportAssignment(node: ExportAssignment) {
617631
if (node.expression.kind === SyntaxKind.Identifier) {
618632
write(node.isExportEquals ? "export = " : "export default ");
619633
writeTextOfNode(currentText, node.expression);
620634
}
621635
else {
622-
// Expression
623-
const tempVarName = getExportDefaultTempVariableName();
624-
if (!noDeclare) {
625-
write("declare ");
626-
}
627-
write("var ");
628-
write(tempVarName);
629-
write(": ");
630-
writer.getSymbolAccessibilityDiagnostic = getDefaultExportAccessibilityDiagnostic;
631-
resolver.writeTypeOfExpression(node.expression, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
632-
write(";");
633-
writeLine();
636+
const tempVarName = emitTempVariableDeclaration(node.expression, "_default", {
637+
diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
638+
errorNode: node
639+
});
634640
write(node.isExportEquals ? "export = " : "export default ");
635641
write(tempVarName);
636642
}
@@ -644,13 +650,6 @@ namespace ts {
644650
// write each of these declarations asynchronously
645651
writeAsynchronousModuleElements(nodes);
646652
}
647-
648-
function getDefaultExportAccessibilityDiagnostic(): SymbolAccessibilityDiagnostic {
649-
return {
650-
diagnosticMessage: Diagnostics.Default_export_of_the_module_has_or_is_using_private_name_0,
651-
errorNode: node
652-
};
653-
}
654653
}
655654

656655
function isModuleElementVisible(node: Declaration) {
@@ -1113,7 +1112,11 @@ namespace ts {
11131112
else {
11141113
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
11151114
errorNameNode = className;
1116-
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
1115+
resolver.writeBaseConstructorTypeOfClass(
1116+
enclosingDeclaration as ClassLikeDeclaration,
1117+
enclosingDeclaration,
1118+
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue,
1119+
writer);
11171120
errorNameNode = undefined;
11181121
}
11191122

@@ -1151,21 +1154,39 @@ namespace ts {
11511154
}
11521155
}
11531156

1157+
const prevEnclosingDeclaration = enclosingDeclaration;
1158+
enclosingDeclaration = node;
1159+
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1160+
let tempVarName: string;
1161+
if (isNonNullExpression(baseTypeNode)) {
1162+
tempVarName = emitTempVariableDeclaration(baseTypeNode.expression, `_${node.name.text}_intersection_base`, {
1163+
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
1164+
errorNode: baseTypeNode,
1165+
typeName: node.name
1166+
});
1167+
}
1168+
11541169
emitJsDocComments(node);
11551170
emitModuleElementDeclarationFlags(node);
11561171
if (hasModifier(node, ModifierFlags.Abstract)) {
11571172
write("abstract ");
11581173
}
1159-
11601174
write("class ");
11611175
writeTextOfNode(currentText, node.name);
1162-
const prevEnclosingDeclaration = enclosingDeclaration;
1163-
enclosingDeclaration = node;
11641176
emitTypeParameters(node.typeParameters);
1165-
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
11661177
if (baseTypeNode) {
1167-
node.name;
1168-
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
1178+
if (isNonNullExpression(baseTypeNode)) {
1179+
write(" extends ");
1180+
write(tempVarName);
1181+
if (baseTypeNode.typeArguments) {
1182+
write("<");
1183+
emitCommaList(baseTypeNode.typeArguments, emitType);
1184+
write(">");
1185+
}
1186+
}
1187+
else {
1188+
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
1189+
}
11691190
}
11701191
emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
11711192
write(" {");
@@ -1201,6 +1222,10 @@ namespace ts {
12011222
enclosingDeclaration = prevEnclosingDeclaration;
12021223
}
12031224

1225+
function isNonNullExpression(node: ExpressionWithTypeArguments) {
1226+
return node && !isEntityNameExpression(node.expression) && node.expression.kind !== SyntaxKind.NullKeyword;
1227+
}
1228+
12041229
function emitPropertyDeclaration(node: Declaration) {
12051230
if (hasDynamicName(node)) {
12061231
return;

0 commit comments

Comments
 (0)