@@ -172,7 +172,6 @@ namespace ts {
172172 EnumTagType,
173173 ResolvedTypeArguments,
174174 ResolvedBaseTypes,
175- WriteType,
176175 }
177176
178177 const enum CheckMode {
@@ -8530,8 +8529,6 @@ namespace ts {
85308529 return !!(target as TypeReference).resolvedTypeArguments;
85318530 case TypeSystemPropertyName.ResolvedBaseTypes:
85328531 return !!(target as InterfaceType).baseTypesResolved;
8533- case TypeSystemPropertyName.WriteType:
8534- return !!getSymbolLinks(target as Symbol).writeType;
85358532 }
85368533 return Debug.assertNever(propertyName);
85378534 }
@@ -9515,11 +9512,6 @@ namespace ts {
95159512 }
95169513 return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression)));
95179514 }
9518- if (isAccessor(declaration)) {
9519- // Binding of certain patterns in JS code will occasionally mark symbols as both properties
9520- // and accessors. Here we dispatch to accessor resolution if needed.
9521- return getTypeOfAccessors(symbol);
9522- }
95239515
95249516 // Handle variable, parameter or property
95259517 if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
@@ -9585,6 +9577,9 @@ namespace ts {
95859577 else if (isEnumMember(declaration)) {
95869578 type = getTypeOfEnumMember(symbol);
95879579 }
9580+ else if (isAccessor(declaration)) {
9581+ type = resolveTypeOfAccessors(symbol) || Debug.fail("Non-write accessor resolution must always produce a type");
9582+ }
95889583 else {
95899584 return Debug.fail("Unhandled declaration kind! " + Debug.formatSyntaxKind(declaration.kind) + " for " + Debug.formatSymbol(symbol));
95909585 }
@@ -9629,62 +9624,97 @@ namespace ts {
96299624
96309625 function getTypeOfAccessors(symbol: Symbol): Type {
96319626 const links = getSymbolLinks(symbol);
9632- if (!links.type) {
9633- if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
9634- return errorType;
9635- }
9627+ return links.type || (links.type = getTypeOfAccessorsWorker(symbol) || Debug.fail("Read type of accessor must always produce a type"));
9628+ }
9629+
9630+ function getTypeOfSetAccessor(symbol: Symbol): Type | undefined {
9631+ const links = getSymbolLinks(symbol);
9632+ return links.writeType || (links.writeType = getTypeOfAccessorsWorker(symbol, /*writing*/ true));
9633+ }
9634+
9635+ function getTypeOfAccessorsWorker(symbol: Symbol, writing = false): Type | undefined {
9636+ if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
9637+ return errorType;
9638+ }
9639+
9640+ let type = resolveTypeOfAccessors(symbol, writing);
9641+ if (!popTypeResolution()) {
96369642 const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
9637- const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
9638- // We try to resolve a getter type annotation, a setter type annotation, or a getter function
9639- // body return type inference, in that order.
9640- let type = getter && isInJSFile(getter) && getTypeForDeclarationFromJSDocComment(getter) ||
9641- getAnnotatedAccessorType(getter) ||
9642- getAnnotatedAccessorType(setter) ||
9643- getter && getter.body && getReturnTypeFromBody(getter);
9644- if (!type) {
9645- if (setter && !isPrivateWithinAmbient(setter)) {
9646- errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
9647- }
9648- else if (getter && !isPrivateWithinAmbient(getter)) {
9649- errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
9650- }
9651- type = anyType;
9652- }
9653- if (!popTypeResolution()) {
9654- if (getAnnotatedAccessorTypeNode(getter)) {
9655- error(getter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
9643+ if (getter) {
9644+ if (getEffectiveTypeAnnotationNode(getter)) {
9645+ error(getter.name, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
96569646 }
9657- else if (getAnnotatedAccessorTypeNode(setter)) {
9658- error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol));
9659- }
9660- else if (getter && noImplicitAny) {
9647+ else if (noImplicitAny) {
96619648 error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
96629649 }
9663- type = anyType;
96649650 }
9665- links. type = type ;
9651+ type = anyType ;
96669652 }
9667- return links. type;
9653+ return type;
96689654 }
96699655
9670- function getWriteTypeOfAccessors(symbol: Symbol): Type {
9671- const links = getSymbolLinks(symbol);
9672- if (!links.writeType) {
9673- if (!pushTypeResolution(symbol, TypeSystemPropertyName.WriteType)) {
9674- return errorType;
9656+ function resolveTypeOfAccessors(symbol: Symbol, writing = false) {
9657+ const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
9658+ const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
9659+
9660+ // For write operations, prioritize type annotations on the setter
9661+ if (writing) {
9662+ const setterType = getAnnotatedAccessorType(setter);
9663+ if (setterType) {
9664+ return instantiateTypeIfNeeded(setterType, symbol);
96759665 }
9676- const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
9677- let writeType = getAnnotatedAccessorType(setter);
9678- if (!popTypeResolution()) {
9679- if (getAnnotatedAccessorTypeNode(setter )) {
9680- error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol) );
9681- }
9682- writeType = anyType ;
9666+ }
9667+ // Else defer to the getter type
9668+
9669+ if (getter && isInJSFile(getter )) {
9670+ const jsDocType = getTypeForDeclarationFromJSDocComment(getter );
9671+ if (jsDocType) {
9672+ return instantiateTypeIfNeeded(jsDocType, symbol) ;
96839673 }
9684- // Absent an explicit setter type annotation we use the read type of the accessor.
9685- links.writeType = writeType || getTypeOfAccessors(symbol);
96869674 }
9687- return links.writeType;
9675+
9676+ // Try to see if the user specified a return type on the get-accessor.
9677+ const getterType = getAnnotatedAccessorType(getter);
9678+ if (getterType) {
9679+ return instantiateTypeIfNeeded(getterType, symbol);
9680+ }
9681+
9682+ // If the user didn't specify a return type, try to use the set-accessor's parameter type.
9683+ const setterType = getAnnotatedAccessorType(setter);
9684+ if (setterType) {
9685+ return setterType;
9686+ }
9687+
9688+ // If there are no specified types, try to infer it from the body of the get accessor if it exists.
9689+ if (getter && getter.body) {
9690+ const returnTypeFromBody = getReturnTypeFromBody(getter);
9691+ return instantiateTypeIfNeeded(returnTypeFromBody, symbol);
9692+ }
9693+
9694+ // Otherwise, fall back to 'any'.
9695+ if (setter) {
9696+ if (!isPrivateWithinAmbient(setter)) {
9697+ errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol));
9698+ }
9699+ return anyType;
9700+ }
9701+ else if (getter) {
9702+ Debug.assert(!!getter, "there must exist a getter as we are current checking either setter or getter in this function");
9703+ if (!isPrivateWithinAmbient(getter)) {
9704+ errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol));
9705+ }
9706+ return anyType;
9707+ }
9708+ return undefined;
9709+
9710+ function instantiateTypeIfNeeded(type: Type, symbol: Symbol) {
9711+ if (getCheckFlags(symbol) & CheckFlags.Instantiated) {
9712+ const links = getSymbolLinks(symbol);
9713+ return instantiateType(type, links.mapper);
9714+ }
9715+
9716+ return type;
9717+ }
96889718 }
96899719
96909720 function getBaseTypeVariableOfClass(symbol: Symbol) {
@@ -9772,12 +9802,17 @@ namespace ts {
97729802
97739803 function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
97749804 const links = getSymbolLinks(symbol);
9775- return links.type || (links.type = instantiateType(getTypeOfSymbol(links.target!), links.mapper));
9776- }
9777-
9778- function getWriteTypeOfInstantiatedSymbol(symbol: Symbol): Type {
9779- const links = getSymbolLinks(symbol);
9780- return links.writeType || (links.writeType = instantiateType(getWriteTypeOfSymbol(links.target!), links.mapper));
9805+ if (!links.type) {
9806+ if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
9807+ return links.type = errorType;
9808+ }
9809+ let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper);
9810+ if (!popTypeResolution()) {
9811+ type = reportCircularityError(symbol);
9812+ }
9813+ links.type = type;
9814+ }
9815+ return links.type;
97819816 }
97829817
97839818 function reportCircularityError(symbol: Symbol) {
@@ -9820,23 +9855,36 @@ namespace ts {
98209855 }
98219856
98229857 /**
9823- * Distinct write types come only from set accessors, but synthetic union and intersection
9824- * properties deriving from set accessors will either pre-compute or defer the union or
9825- * intersection of the writeTypes of their constituents.
9858+ * Distinct write types come only from set accessors, but union and intersection
9859+ * properties deriving from set accessors will either pre-compute or defer the
9860+ * union or intersection of the writeTypes of their constituents. To account for
9861+ * this, we will assume that any deferred type or transient symbol may have a
9862+ * `writeType` (or a deferred write type ready to be computed) that should be
9863+ * used before looking for set accessor declarations.
98269864 */
98279865 function getWriteTypeOfSymbol(symbol: Symbol): Type {
98289866 const checkFlags = getCheckFlags(symbol);
9829- if (symbol.flags & SymbolFlags.Property) {
9830- return checkFlags & CheckFlags.SyntheticProperty ?
9831- checkFlags & CheckFlags.DeferredType ?
9832- getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) :
9833- (symbol as TransientSymbol).writeType || (symbol as TransientSymbol).type! :
9834- getTypeOfSymbol(symbol);
9867+ if (checkFlags & CheckFlags.DeferredType) {
9868+ const writeType = getWriteTypeOfSymbolWithDeferredType(symbol);
9869+ if (writeType) {
9870+ return writeType;
9871+ }
9872+ }
9873+ if (symbol.flags & SymbolFlags.Transient) {
9874+ const { writeType } = symbol as TransientSymbol;
9875+ if (writeType) {
9876+ return writeType;
9877+ }
98359878 }
9879+ return getSetAccessorTypeOfSymbol(symbol);
9880+ }
9881+
9882+ function getSetAccessorTypeOfSymbol(symbol: Symbol): Type {
98369883 if (symbol.flags & SymbolFlags.Accessor) {
9837- return checkFlags & CheckFlags.Instantiated ?
9838- getWriteTypeOfInstantiatedSymbol(symbol) :
9839- getWriteTypeOfAccessors(symbol);
9884+ const type = getTypeOfSetAccessor(symbol);
9885+ if (type) {
9886+ return type;
9887+ }
98409888 }
98419889 return getTypeOfSymbol(symbol);
98429890 }
@@ -25199,7 +25247,7 @@ namespace ts {
2519925247 }
2520025248 }
2520125249 if (isDeclarationName(location) && isSetAccessor(location.parent) && getAnnotatedAccessorTypeNode(location.parent)) {
25202- return getWriteTypeOfAccessors (location.parent.symbol) ;
25250+ return resolveTypeOfAccessors (location.parent.symbol, /*writing*/ true)! ;
2520325251 }
2520425252 // The location isn't a reference to the given symbol, meaning we're being asked
2520525253 // a hypothetical question of what type the symbol would have if there was a reference
0 commit comments