@@ -4713,97 +4713,35 @@ namespace ts {
47134713 }
47144714 return getWidenedLiteralType(checkExpressionCached(specialDeclaration));
47154715 }
4716- const types: Type[] = [];
4717- let constructorTypes: Type[] | undefined;
47184716 let definedInConstructor = false;
47194717 let definedInMethod = false;
4720- let jsDocType: Type | undefined;
4718+ let jsdocType: Type | undefined;
4719+ let types: Type[] | undefined;
47214720 for (const declaration of symbol.declarations) {
4722- let declarationInConstructor = false;
47234721 const expression = isBinaryExpression(declaration) ? declaration :
47244722 isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration :
47254723 undefined;
4726-
47274724 if (!expression) {
47284725 return errorType;
47294726 }
47304727
47314728 const special = isPropertyAccessExpression(expression) ? getSpecialPropertyAccessKind(expression) : getSpecialPropertyAssignmentKind(expression);
47324729 if (special === SpecialPropertyAssignmentKind.ThisProperty) {
4733- const thisContainer = getThisContainer(expression, /*includeArrowFunctions*/ false);
4734- // Properties defined in a constructor (or base constructor, or javascript constructor function) don't get undefined added.
4735- // Function expressions that are assigned to the prototype count as methods.
4736- declarationInConstructor = thisContainer.kind === SyntaxKind.Constructor ||
4737- thisContainer.kind === SyntaxKind.FunctionDeclaration ||
4738- (thisContainer.kind === SyntaxKind.FunctionExpression && !isPrototypePropertyAssignment(thisContainer.parent));
4739- if (declarationInConstructor) {
4730+ if (isDeclarationInConstructor(expression)) {
47404731 definedInConstructor = true;
47414732 }
47424733 else {
47434734 definedInMethod = true;
47444735 }
47454736 }
4746-
4747- // If there is a JSDoc type, use it
4748- const type = getTypeForDeclarationFromJSDocComment(expression.parent);
4749- if (type) {
4750- const declarationType = getWidenedType(type);
4751- if (!jsDocType) {
4752- jsDocType = declarationType;
4753- }
4754- else if (jsDocType !== errorType && declarationType !== errorType &&
4755- !isTypeIdenticalTo(jsDocType, declarationType) &&
4756- !(symbol.flags & SymbolFlags.JSContainer)) {
4757- errorNextVariableOrPropertyDeclarationMustHaveSameType(jsDocType, declaration, declarationType);
4758- }
4759- }
4760- else if (!jsDocType && isBinaryExpression(expression)) {
4761- // If we don't have an explicit JSDoc type, get the type from the expression.
4762- let type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right));
4763-
4764- if (type.flags & TypeFlags.Object &&
4765- special === SpecialPropertyAssignmentKind.ModuleExports &&
4766- symbol.escapedName === InternalSymbolName.ExportEquals) {
4767- const exportedType = resolveStructuredTypeMembers(type as ObjectType);
4768- const members = createSymbolTable();
4769- copyEntries(exportedType.members, members);
4770- if (resolvedSymbol && !resolvedSymbol.exports) {
4771- resolvedSymbol.exports = createSymbolTable();
4772- }
4773- (resolvedSymbol || symbol).exports!.forEach((s, name) => {
4774- if (members.has(name)) {
4775- const exportedMember = exportedType.members.get(name)!;
4776- const union = createSymbol(s.flags | exportedMember.flags, name);
4777- union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]);
4778- members.set(name, union);
4779- }
4780- else {
4781- members.set(name, s);
4782- }
4783- });
4784- type = createAnonymousType(
4785- exportedType.symbol,
4786- members,
4787- exportedType.callSignatures,
4788- exportedType.constructSignatures,
4789- exportedType.stringIndexInfo,
4790- exportedType.numberIndexInfo);
4791- }
4792- let anyedType = type;
4793- if (isEmptyArrayLiteralType(type)) {
4794- anyedType = anyArrayType;
4795- if (noImplicitAny) {
4796- reportImplicitAnyError(expression, anyArrayType);
4797- }
4798- }
4799- types.push(anyedType);
4800- if (declarationInConstructor) {
4801- (constructorTypes || (constructorTypes = [])).push(anyedType);
4802- }
4737+ jsdocType = getJSDocTypeFromSpecialDeclarations(jsdocType, expression, symbol, declaration);
4738+ if (!jsdocType) {
4739+ (types || (types = [])).push(isBinaryExpression(expression) ? getInitializerTypeFromSpecialDeclarations(symbol, resolvedSymbol, expression, special) : neverType);
48034740 }
48044741 }
4805- let type = jsDocType ;
4742+ let type = jsdocType ;
48064743 if (!type) {
4744+ let constructorTypes = definedInConstructor ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined;
48074745 // use only the constructor types unless they were only assigned null | undefined (including widening variants)
48084746 if (definedInMethod) {
48094747 const propType = getTypeOfSpecialPropertyOfBaseType(symbol);
@@ -4812,8 +4750,8 @@ namespace ts {
48124750 definedInConstructor = true;
48134751 }
48144752 }
4815- const sourceTypes = some(constructorTypes, t => !!(t.flags & ~(TypeFlags.Nullable | TypeFlags.ContainsWideningType))) ? constructorTypes! : types; // TODO: GH#18217
4816- type = getUnionType(sourceTypes, UnionReduction.Subtype);
4753+ const sourceTypes = some(constructorTypes, t => !!(t.flags & ~(TypeFlags.Nullable | TypeFlags.ContainsWideningType))) ? constructorTypes : types; // TODO: GH#18217
4754+ type = getUnionType(sourceTypes! , UnionReduction.Subtype);
48174755 }
48184756 const widened = getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
48194757 if (filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) === neverType) {
@@ -4825,6 +4763,82 @@ namespace ts {
48254763 return widened;
48264764 }
48274765
4766+ function getJSDocTypeFromSpecialDeclarations(declaredType: Type | undefined, expression: Expression, _symbol: Symbol, declaration: Declaration) {
4767+ const typeNode = getJSDocType(expression.parent);
4768+ if (typeNode) {
4769+ const type = getWidenedType(getTypeFromTypeNode(typeNode));
4770+ if (!declaredType) {
4771+ return type;
4772+ }
4773+ else if (declaredType !== errorType && type !== errorType && !isTypeIdenticalTo(declaredType, type)) {
4774+ errorNextVariableOrPropertyDeclarationMustHaveSameType(declaredType, declaration, type);
4775+ }
4776+ }
4777+ return declaredType;
4778+ }
4779+
4780+ /** If we don't have an explicit JSDoc type, get the type from the initializer. */
4781+ function getInitializerTypeFromSpecialDeclarations(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: Expression, special: SpecialPropertyAssignmentKind) {
4782+ if (isBinaryExpression(expression)) {
4783+ const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right));
4784+ if (type.flags & TypeFlags.Object &&
4785+ special === SpecialPropertyAssignmentKind.ModuleExports &&
4786+ symbol.escapedName === InternalSymbolName.ExportEquals) {
4787+ const exportedType = resolveStructuredTypeMembers(type as ObjectType);
4788+ const members = createSymbolTable();
4789+ copyEntries(exportedType.members, members);
4790+ if (resolvedSymbol && !resolvedSymbol.exports) {
4791+ resolvedSymbol.exports = createSymbolTable();
4792+ }
4793+ (resolvedSymbol || symbol).exports!.forEach((s, name) => {
4794+ if (members.has(name)) {
4795+ const exportedMember = exportedType.members.get(name)!;
4796+ const union = createSymbol(s.flags | exportedMember.flags, name);
4797+ union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]);
4798+ members.set(name, union);
4799+ }
4800+ else {
4801+ members.set(name, s);
4802+ }
4803+ });
4804+ return createAnonymousType(
4805+ exportedType.symbol,
4806+ members,
4807+ exportedType.callSignatures,
4808+ exportedType.constructSignatures,
4809+ exportedType.stringIndexInfo,
4810+ exportedType.numberIndexInfo);
4811+ }
4812+ if (isEmptyArrayLiteralType(type)) {
4813+ if (noImplicitAny) {
4814+ reportImplicitAnyError(expression, anyArrayType);
4815+ }
4816+ return anyArrayType;
4817+ }
4818+ return type;
4819+ }
4820+ return neverType;
4821+ }
4822+
4823+ function isDeclarationInConstructor(expression: Expression) {
4824+ const thisContainer = getThisContainer(expression, /*includeArrowFunctions*/ false);
4825+ // Properties defined in a constructor (or base constructor, or javascript constructor function) don't get undefined added.
4826+ // Function expressions that are assigned to the prototype count as methods.
4827+ return thisContainer.kind === SyntaxKind.Constructor ||
4828+ thisContainer.kind === SyntaxKind.FunctionDeclaration ||
4829+ (thisContainer.kind === SyntaxKind.FunctionExpression && !isPrototypePropertyAssignment(thisContainer.parent));
4830+ }
4831+
4832+ function getConstructorDefinedThisAssignmentTypes(types: Type[], declarations: Declaration[]): Type[] | undefined {
4833+ Debug.assert(types.length === declarations.length);
4834+ return types.filter((_, i) => {
4835+ const declaration = declarations[i];
4836+ const expression = isBinaryExpression(declaration) ? declaration :
4837+ isBinaryExpression(declaration.parent) ? declaration.parent : undefined;
4838+ return expression && isDeclarationInConstructor(expression);
4839+ });
4840+ }
4841+
48284842 /** check for definition in base class if any declaration is in a class */
48294843 function getTypeOfSpecialPropertyOfBaseType(specialProperty: Symbol) {
48304844 const parentDeclaration = forEach(specialProperty.declarations, d => {
0 commit comments