@@ -4713,97 +4713,35 @@ namespace ts {
4713
4713
}
4714
4714
return getWidenedLiteralType(checkExpressionCached(specialDeclaration));
4715
4715
}
4716
- const types: Type[] = [];
4717
- let constructorTypes: Type[] | undefined;
4718
4716
let definedInConstructor = false;
4719
4717
let definedInMethod = false;
4720
- let jsDocType: Type | undefined;
4718
+ let jsdocType: Type | undefined;
4719
+ let types: Type[] | undefined;
4721
4720
for (const declaration of symbol.declarations) {
4722
- let declarationInConstructor = false;
4723
4721
const expression = isBinaryExpression(declaration) ? declaration :
4724
4722
isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration :
4725
4723
undefined;
4726
-
4727
4724
if (!expression) {
4728
4725
return errorType;
4729
4726
}
4730
4727
4731
4728
const special = isPropertyAccessExpression(expression) ? getSpecialPropertyAccessKind(expression) : getSpecialPropertyAssignmentKind(expression);
4732
4729
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)) {
4740
4731
definedInConstructor = true;
4741
4732
}
4742
4733
else {
4743
4734
definedInMethod = true;
4744
4735
}
4745
4736
}
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);
4803
4740
}
4804
4741
}
4805
- let type = jsDocType ;
4742
+ let type = jsdocType ;
4806
4743
if (!type) {
4744
+ let constructorTypes = definedInConstructor ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined;
4807
4745
// use only the constructor types unless they were only assigned null | undefined (including widening variants)
4808
4746
if (definedInMethod) {
4809
4747
const propType = getTypeOfSpecialPropertyOfBaseType(symbol);
@@ -4812,8 +4750,8 @@ namespace ts {
4812
4750
definedInConstructor = true;
4813
4751
}
4814
4752
}
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);
4817
4755
}
4818
4756
const widened = getWidenedType(addOptionality(type, definedInMethod && !definedInConstructor));
4819
4757
if (filterType(widened, t => !!(t.flags & ~TypeFlags.Nullable)) === neverType) {
@@ -4825,6 +4763,82 @@ namespace ts {
4825
4763
return widened;
4826
4764
}
4827
4765
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
+
4828
4842
/** check for definition in base class if any declaration is in a class */
4829
4843
function getTypeOfSpecialPropertyOfBaseType(specialProperty: Symbol) {
4830
4844
const parentDeclaration = forEach(specialProperty.declarations, d => {
0 commit comments