Skip to content

Commit cd272e8

Browse files
committed
Error on emit declaration of extends class w/o symbol
Error when emitting an extends clause for a type that has no symbol. This error only occurs on exported classes. This prevents the emitter from producing types that extend from intersections, which are not parseable right now.
1 parent 58b8a54 commit cd272e8

File tree

6 files changed

+29
-7
lines changed

6 files changed

+29
-7
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20844,6 +20844,9 @@ namespace ts {
2084420844
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node));
2084520845
resolveBaseTypesOfClass(classType);
2084620846
const baseType = classType.resolvedBaseTypes.length ? classType.resolvedBaseTypes[0] : unknownType;
20847+
if (!baseType.symbol) {
20848+
writer.reportIllegalExtends();
20849+
}
2084720850
getSymbolDisplayBuilder().buildTypeDisplay(baseType, writer, enclosingDeclaration, flags);
2084820851
}
2084920852

src/compiler/declarationEmitter.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ namespace ts {
190190
const writer = <EmitTextWriterWithSymbolWriter>createTextWriter(newLine);
191191
writer.trackSymbol = trackSymbol;
192192
writer.reportInaccessibleThisError = reportInaccessibleThisError;
193+
writer.reportIllegalExtends = reportIllegalExtends;
193194
writer.writeKeyword = writer.write;
194195
writer.writeOperator = writer.write;
195196
writer.writePunctuation = writer.write;
@@ -313,6 +314,14 @@ namespace ts {
313314
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
314315
}
315316

317+
function reportIllegalExtends() {
318+
if (errorNameNode) {
319+
reportedDeclarationError = true;
320+
emitterDiagnostics.add(createDiagnosticForNode(errorNameNode, Diagnostics.Extends_clause_of_exported_class_0_refers_to_a_type_with_no_declaration,
321+
declarationNameToString(errorNameNode)));
322+
}
323+
}
324+
316325
function reportInaccessibleThisError() {
317326
if (errorNameNode) {
318327
reportedDeclarationError = true;
@@ -1071,7 +1080,7 @@ namespace ts {
10711080
}
10721081
}
10731082

1074-
function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
1083+
function emitHeritageClause(className: Identifier, typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
10751084
if (typeReferences) {
10761085
write(isImplementsList ? " implements " : " extends ");
10771086
emitCommaList(typeReferences, emitTypeOfTypeReference);
@@ -1086,7 +1095,9 @@ namespace ts {
10861095
}
10871096
else {
10881097
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
1098+
errorNameNode = className;
10891099
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
1100+
errorNameNode = undefined;
10901101
}
10911102

10921103
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
@@ -1136,9 +1147,10 @@ namespace ts {
11361147
emitTypeParameters(node.typeParameters);
11371148
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
11381149
if (baseTypeNode) {
1139-
emitHeritageClause([baseTypeNode], /*isImplementsList*/ false);
1150+
node.name
1151+
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
11401152
}
1141-
emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
1153+
emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
11421154
write(" {");
11431155
writeLine();
11441156
increaseIndent();
@@ -1160,7 +1172,7 @@ namespace ts {
11601172
emitTypeParameters(node.typeParameters);
11611173
const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression));
11621174
if (interfaceExtendsTypes && interfaceExtendsTypes.length) {
1163-
emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false);
1175+
emitHeritageClause(node.name, interfaceExtendsTypes, /*isImplementsList*/ false);
11641176
}
11651177
write(" {");
11661178
writeLine();

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,10 @@
23522352
"category": "Error",
23532353
"code": 4092
23542354
},
2355+
"Extends clause of exported class '{0}' refers to a type with no declaration.": {
2356+
"category": "Error",
2357+
"code": 4093
2358+
},
23552359

23562360
"The current host does not support the '{0}' option.": {
23572361
"category": "Error",

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,6 +2459,7 @@
24592459
// with import statements it previously saw (but chose not to emit).
24602460
trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
24612461
reportInaccessibleThisError(): void;
2462+
reportIllegalExtends(): void;
24622463
}
24632464

24642465
export const enum TypeFormatFlags {

src/compiler/utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ namespace ts {
5353
decreaseIndent: noop,
5454
clear: () => str = "",
5555
trackSymbol: noop,
56-
reportInaccessibleThisError: noop
56+
reportInaccessibleThisError: noop,
57+
reportIllegalExtends: noop
5758
};
5859
}
5960

src/services/utilities.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,8 @@ namespace ts {
11661166
decreaseIndent: () => { indent--; },
11671167
clear: resetWriter,
11681168
trackSymbol: noop,
1169-
reportInaccessibleThisError: noop
1169+
reportInaccessibleThisError: noop,
1170+
reportIllegalExtends: noop
11701171
};
11711172

11721173
function writeIndent() {
@@ -1386,4 +1387,4 @@ namespace ts {
13861387
// First token is the open curly, this is where we want to put the 'super' call.
13871388
return constructor.body.getFirstToken(sourceFile).getEnd();
13881389
}
1389-
}
1390+
}

0 commit comments

Comments
 (0)