Skip to content

Commit 4303a29

Browse files
authored
Merge pull request microsoft#15537 from Microsoft/emit-declaration-of-class-extending-intersection
Emit declaration of class extending intersection
2 parents 1511c2e + 58e72fe commit 4303a29

23 files changed

+303
-128
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22750,16 +22750,6 @@ namespace ts {
2275022750
getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags);
2275122751
}
2275222752

22753-
function writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
22754-
const classType = <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node));
22755-
resolveBaseTypesOfClass(classType);
22756-
const baseType = classType.resolvedBaseTypes.length ? classType.resolvedBaseTypes[0] : unknownType;
22757-
if (!baseType.symbol) {
22758-
writer.reportIllegalExtends();
22759-
}
22760-
getSymbolDisplayBuilder().buildTypeDisplay(baseType, writer, enclosingDeclaration, flags);
22761-
}
22762-
2276322753
function hasGlobalName(name: string): boolean {
2276422754
return globals.has(name);
2276522755
}
@@ -22853,7 +22843,6 @@ namespace ts {
2285322843
writeTypeOfDeclaration,
2285422844
writeReturnTypeOfSignatureDeclaration,
2285522845
writeTypeOfExpression,
22856-
writeBaseConstructorTypeOfClass,
2285722846
isSymbolAccessible,
2285822847
isEntityNameVisible,
2285922848
getConstantValue: node => {

src/compiler/declarationEmitter.ts

Lines changed: 50 additions & 37 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("const ");
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) {
@@ -1097,7 +1096,7 @@ namespace ts {
10971096
}
10981097
}
10991098

1100-
function emitHeritageClause(className: Identifier, typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
1099+
function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) {
11011100
if (typeReferences) {
11021101
write(isImplementsList ? " implements " : " extends ");
11031102
emitCommaList(typeReferences, emitTypeOfTypeReference);
@@ -1110,12 +1109,6 @@ namespace ts {
11101109
else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) {
11111110
write("null");
11121111
}
1113-
else {
1114-
writer.getSymbolAccessibilityDiagnostic = getHeritageClauseVisibilityError;
1115-
errorNameNode = className;
1116-
resolver.writeBaseConstructorTypeOfClass(<ClassLikeDeclaration>enclosingDeclaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer);
1117-
errorNameNode = undefined;
1118-
}
11191112

11201113
function getHeritageClauseVisibilityError(): SymbolAccessibilityDiagnostic {
11211114
let diagnosticMessage: DiagnosticMessage;
@@ -1151,23 +1144,43 @@ namespace ts {
11511144
}
11521145
}
11531146

1147+
const prevEnclosingDeclaration = enclosingDeclaration;
1148+
enclosingDeclaration = node;
1149+
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1150+
let tempVarName: string;
1151+
if (baseTypeNode && !isEntityNameExpression(baseTypeNode.expression)) {
1152+
tempVarName = baseTypeNode.expression.kind === SyntaxKind.NullKeyword ?
1153+
"null" :
1154+
emitTempVariableDeclaration(baseTypeNode.expression, `${node.name.text}_base`, {
1155+
diagnosticMessage: Diagnostics.extends_clause_of_exported_class_0_has_or_is_using_private_name_1,
1156+
errorNode: baseTypeNode,
1157+
typeName: node.name
1158+
});
1159+
}
1160+
11541161
emitJsDocComments(node);
11551162
emitModuleElementDeclarationFlags(node);
11561163
if (hasModifier(node, ModifierFlags.Abstract)) {
11571164
write("abstract ");
11581165
}
1159-
11601166
write("class ");
11611167
writeTextOfNode(currentText, node.name);
1162-
const prevEnclosingDeclaration = enclosingDeclaration;
1163-
enclosingDeclaration = node;
11641168
emitTypeParameters(node.typeParameters);
1165-
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
11661169
if (baseTypeNode) {
1167-
node.name;
1168-
emitHeritageClause(node.name, [baseTypeNode], /*isImplementsList*/ false);
1170+
if (!isEntityNameExpression(baseTypeNode.expression)) {
1171+
write(" extends ");
1172+
write(tempVarName);
1173+
if (baseTypeNode.typeArguments) {
1174+
write("<");
1175+
emitCommaList(baseTypeNode.typeArguments, emitType);
1176+
write(">");
1177+
}
1178+
}
1179+
else {
1180+
emitHeritageClause([baseTypeNode], /*isImplementsList*/ false);
1181+
}
11691182
}
1170-
emitHeritageClause(node.name, getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
1183+
emitHeritageClause(getClassImplementsHeritageClauseElements(node), /*isImplementsList*/ true);
11711184
write(" {");
11721185
writeLine();
11731186
increaseIndent();
@@ -1189,7 +1202,7 @@ namespace ts {
11891202
emitTypeParameters(node.typeParameters);
11901203
const interfaceExtendsTypes = filter(getInterfaceBaseTypeNodes(node), base => isEntityNameExpression(base.expression));
11911204
if (interfaceExtendsTypes && interfaceExtendsTypes.length) {
1192-
emitHeritageClause(node.name, interfaceExtendsTypes, /*isImplementsList*/ false);
1205+
emitHeritageClause(interfaceExtendsTypes, /*isImplementsList*/ false);
11931206
}
11941207
write(" {");
11951208
writeLine();

src/compiler/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2734,7 +2734,6 @@ namespace ts {
27342734
writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
27352735
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
27362736
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
2737-
writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
27382737
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
27392738
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
27402739
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant

tests/baselines/reference/declarationEmitDefaultExport5.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ export default 1 + 2;
77

88

99
//// [declarationEmitDefaultExport5.d.ts]
10-
declare var _default: number;
10+
declare const _default: number;
1111
export default _default;

tests/baselines/reference/declarationEmitDefaultExport6.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ export default new A();
1212
//// [declarationEmitDefaultExport6.d.ts]
1313
export declare class A {
1414
}
15-
declare var _default: A;
15+
declare const _default: A;
1616
export default _default;

tests/baselines/reference/declarationEmitDefaultExport8.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ export default 1 + 2;
1313
//// [declarationEmitDefaultExport8.d.ts]
1414
declare var _default: number;
1515
export { _default as d };
16-
declare var _default_1: number;
16+
declare const _default_1: number;
1717
export default _default_1;

tests/baselines/reference/declarationEmitDefaultExportWithTempVarName.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ System.register([], function (exports_1, context_1) {
1515

1616

1717
//// [pi.d.ts]
18-
declare var _default: 3.14159;
18+
declare const _default: 3.14159;
1919
export default _default;

tests/baselines/reference/declarationEmitDefaultExportWithTempVarNameWithBundling.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ System.register("pi", [], function (exports_1, context_1) {
1616

1717
//// [app.d.ts]
1818
declare module "pi" {
19-
var _default: 3.14159;
19+
const _default: 3.14159;
2020
export default _default;
2121
}

tests/baselines/reference/declarationEmitExpressionInExtends2.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,6 @@ declare class C<T, U> {
4545
y: U;
4646
}
4747
declare function getClass<T>(c: T): typeof C;
48-
declare class MyClass extends C<string, number> {
48+
declare const MyClass_base: typeof C;
49+
declare class MyClass extends MyClass_base<string, number> {
4950
}

tests/baselines/reference/declarationEmitExpressionInExtends3.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
tests/cases/compiler/declarationEmitExpressionInExtends3.ts(28,30): error TS4020: 'extends' clause of exported class 'MyClass' has or is using private name 'LocalClass'.
2-
tests/cases/compiler/declarationEmitExpressionInExtends3.ts(36,31): error TS4020: 'extends' clause of exported class 'MyClass3' has or is using private name 'LocalInterface'.
2+
tests/cases/compiler/declarationEmitExpressionInExtends3.ts(36,75): error TS4020: 'extends' clause of exported class 'MyClass3' has or is using private name 'LocalInterface'.
33

44

55
==== tests/cases/compiler/declarationEmitExpressionInExtends3.ts (2 errors) ====
@@ -41,7 +41,7 @@ tests/cases/compiler/declarationEmitExpressionInExtends3.ts(36,31): error TS4020
4141

4242

4343
export class MyClass3 extends getExportedClass<LocalInterface>(undefined)<LocalInterface> { // Error LocalInterface is inaccisble
44-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44+
~~~~~~~~~~~~~~
4545
!!! error TS4020: 'extends' clause of exported class 'MyClass3' has or is using private name 'LocalInterface'.
4646
}
4747

tests/baselines/reference/declarationEmitExpressionInExtends4.errors.txt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(1,10): error TS4060: Return type of exported function has or is using private name 'D'.
2-
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(5,7): error TS4093: 'extends' clause of exported class 'C' refers to a type whose name cannot be referenced.
32
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(5,17): error TS2315: Type 'D' is not generic.
4-
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(9,7): error TS4093: 'extends' clause of exported class 'C2' refers to a type whose name cannot be referenced.
3+
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(5,17): error TS4020: 'extends' clause of exported class 'C' has or is using private name 'D'.
54
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(9,18): error TS2304: Cannot find name 'SomeUndefinedFunction'.
65
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(14,18): error TS2304: Cannot find name 'SomeUndefinedFunction'.
76
tests/cases/compiler/declarationEmitExpressionInExtends4.ts(14,18): error TS4020: 'extends' clause of exported class 'C3' has or is using private name 'SomeUndefinedFunction'.
87

98

10-
==== tests/cases/compiler/declarationEmitExpressionInExtends4.ts (7 errors) ====
9+
==== tests/cases/compiler/declarationEmitExpressionInExtends4.ts (6 errors) ====
1110
function getSomething() {
1211
~~~~~~~~~~~~
1312
!!! error TS4060: Return type of exported function has or is using private name 'D'.
1413
return class D { }
1514
}
1615

1716
class C extends getSomething()<number, string> {
18-
~
19-
!!! error TS4093: 'extends' clause of exported class 'C' refers to a type whose name cannot be referenced.
2017
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2118
!!! error TS2315: Type 'D' is not generic.
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! error TS4020: 'extends' clause of exported class 'C' has or is using private name 'D'.
2221

2322
}
2423

2524
class C2 extends SomeUndefinedFunction()<number, string> {
26-
~~
27-
!!! error TS4093: 'extends' clause of exported class 'C2' refers to a type whose name cannot be referenced.
2825
~~~~~~~~~~~~~~~~~~~~~
2926
!!! error TS2304: Cannot find name 'SomeUndefinedFunction'.
3027

tests/baselines/reference/declarationEmitInferedDefaultExportType.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ exports["default"] = {
1818

1919

2020
//// [declarationEmitInferedDefaultExportType.d.ts]
21-
declare var _default: {
21+
declare const _default: {
2222
foo: any[];
2323
bar: any;
2424
baz: any;

tests/baselines/reference/declarationEmitInferedDefaultExportType2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = {
1616

1717

1818
//// [declarationEmitInferedDefaultExportType2.d.ts]
19-
declare var _default: {
19+
declare const _default: {
2020
foo: any[];
2121
bar: any;
2222
baz: any;

tests/baselines/reference/es5ExportDefaultExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ exports.default = (1 + 2);
99

1010

1111
//// [es5ExportDefaultExpression.d.ts]
12-
declare var _default: number;
12+
declare const _default: number;
1313
export default _default;

tests/baselines/reference/es6ExportDefaultExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ export default (1 + 2);
77

88

99
//// [es6ExportDefaultExpression.d.ts]
10-
declare var _default: number;
10+
declare const _default: number;
1111
export default _default;

tests/baselines/reference/es6ImportDefaultBindingFollowedWithNamedImport.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ var x1 = es6ImportDefaultBindingFollowedWithNamedImport_0_5.m;
4848
export declare var a: number;
4949
export declare var x: number;
5050
export declare var m: number;
51-
declare var _default: {};
51+
declare const _default: {};
5252
export default _default;
5353
//// [es6ImportDefaultBindingFollowedWithNamedImport_1.d.ts]

tests/baselines/reference/es6ImportDefaultBindingFollowedWithNamedImportWithExport.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ define(["require", "exports", "server", "server", "server", "server", "server"],
4747
export declare var a: number;
4848
export declare var x: number;
4949
export declare var m: number;
50-
declare var _default: {};
50+
declare const _default: {};
5151
export default _default;
5252
//// [client.d.ts]
5353
export declare var x1: number;

tests/baselines/reference/exportClassExtendingIntersection.errors.txt

Lines changed: 0 additions & 39 deletions
This file was deleted.

tests/baselines/reference/exportClassExtendingIntersection.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,11 @@ export interface MyMixin {
111111
mixinProperty: string;
112112
}
113113
export declare function MyMixin<T extends Constructor<MyBaseClass<any>>>(base: T): T & Constructor<MyMixin>;
114+
//// [FinalClass.d.ts]
115+
import { MyBaseClass } from './BaseClass';
116+
import { MyMixin } from './MixinClass';
117+
declare const MyExtendedClass_base: typeof MyBaseClass & (new (...args: any[]) => MyMixin);
118+
export declare class MyExtendedClass extends MyExtendedClass_base<string> {
119+
extendedClassProperty: number;
120+
}
114121
//// [Main.d.ts]

0 commit comments

Comments
 (0)