@@ -5949,6 +5949,25 @@ namespace ts {
59495949 }
59505950 }
59515951
5952+ function isEffectiveClassSymbol(symbol: Symbol) {
5953+ if (!(symbol.flags & SymbolFlags.Class)) {
5954+ return false;
5955+ }
5956+ if (isInJSFile(symbol.valueDeclaration) && !isClassLike(symbol.valueDeclaration)) {
5957+ // For a symbol that isn't syntactically a `class` in a JS file we have heuristics
5958+ // that detect prototype assignments that indicate the symbol is *probably* a class.
5959+ // Filter out any prototype assignments for non-class symbols, i.e.
5960+ //
5961+ // let A;
5962+ // A = {};
5963+ // A.prototype.b = {};
5964+ const type = getTypeOfSymbol(symbol);
5965+ return some(getSignaturesOfType(type, SignatureKind.Construct))
5966+ || some(getSignaturesOfType(type, SignatureKind.Call));
5967+ }
5968+ return true;
5969+ }
5970+
59525971 // Synthesize declarations for a symbol - might be an Interface, a Class, a Namespace, a Type, a Variable (const, let, or var), an Alias
59535972 // or a merge of some number of those.
59545973 // An interesting challenge is ensuring that when classes merge with namespaces and interfaces, is keeping
@@ -5990,14 +6009,14 @@ namespace ts {
59906009 if (symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property)
59916010 && symbol.escapedName !== InternalSymbolName.ExportEquals
59926011 && !(symbol.flags & SymbolFlags.Prototype)
5993- && !(symbol.flags & SymbolFlags.Class )
6012+ && !isEffectiveClassSymbol (symbol)
59946013 && !isConstMergedWithNSPrintableAsSignatureMerge) {
59956014 serializeVariableOrProperty(symbol, symbolName, isPrivate, needsPostExportDefault, propertyAsAlias, modifierFlags);
59966015 }
59976016 if (symbol.flags & SymbolFlags.Enum) {
59986017 serializeEnum(symbol, symbolName, modifierFlags);
59996018 }
6000- if (symbol.flags & SymbolFlags.Class ) {
6019+ if (isEffectiveClassSymbol( symbol) ) {
60016020 if (symbol.flags & SymbolFlags.Property && isBinaryExpression(symbol.valueDeclaration.parent) && isClassExpression(symbol.valueDeclaration.parent.right)) {
60026021 // Looks like a `module.exports.Sub = class {}` - if we serialize `symbol` as a class, the result will have no members,
60036022 // since the classiness is actually from the target of the effective alias the symbol is. yes. A BlockScopedVariable|Class|Property
@@ -6317,7 +6336,9 @@ namespace ts {
63176336 const baseTypes = getBaseTypes(classType);
63186337 const implementsTypes = getImplementsTypes(classType);
63196338 const staticType = getTypeOfSymbol(symbol);
6320- const staticBaseType = getBaseConstructorTypeOfClass(staticType as InterfaceType);
6339+ const staticBaseType = staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration)
6340+ ? getBaseConstructorTypeOfClass(staticType as InterfaceType)
6341+ : anyType;
63216342 const heritageClauses = [
63226343 ...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
63236344 ...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
0 commit comments