@@ -172,7 +172,6 @@ namespace ts {
172
172
EnumTagType,
173
173
ResolvedTypeArguments,
174
174
ResolvedBaseTypes,
175
- WriteType,
176
175
}
177
176
178
177
const enum CheckMode {
@@ -8530,8 +8529,6 @@ namespace ts {
8530
8529
return !!(target as TypeReference).resolvedTypeArguments;
8531
8530
case TypeSystemPropertyName.ResolvedBaseTypes:
8532
8531
return !!(target as InterfaceType).baseTypesResolved;
8533
- case TypeSystemPropertyName.WriteType:
8534
- return !!getSymbolLinks(target as Symbol).writeType;
8535
8532
}
8536
8533
return Debug.assertNever(propertyName);
8537
8534
}
@@ -9515,11 +9512,6 @@ namespace ts {
9515
9512
}
9516
9513
return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression)));
9517
9514
}
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
- }
9523
9515
9524
9516
// Handle variable, parameter or property
9525
9517
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
@@ -9585,6 +9577,9 @@ namespace ts {
9585
9577
else if (isEnumMember(declaration)) {
9586
9578
type = getTypeOfEnumMember(symbol);
9587
9579
}
9580
+ else if (isAccessor(declaration)) {
9581
+ type = resolveTypeOfAccessors(symbol) || Debug.fail("Non-write accessor resolution must always produce a type");
9582
+ }
9588
9583
else {
9589
9584
return Debug.fail("Unhandled declaration kind! " + Debug.formatSyntaxKind(declaration.kind) + " for " + Debug.formatSymbol(symbol));
9590
9585
}
@@ -9629,62 +9624,97 @@ namespace ts {
9629
9624
9630
9625
function getTypeOfAccessors(symbol: Symbol): Type {
9631
9626
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()) {
9636
9642
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));
9656
9646
}
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) {
9661
9648
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));
9662
9649
}
9663
- type = anyType;
9664
9650
}
9665
- links. type = type ;
9651
+ type = anyType ;
9666
9652
}
9667
- return links. type;
9653
+ return type;
9668
9654
}
9669
9655
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);
9675
9665
}
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) ;
9683
9673
}
9684
- // Absent an explicit setter type annotation we use the read type of the accessor.
9685
- links.writeType = writeType || getTypeOfAccessors(symbol);
9686
9674
}
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
+ }
9688
9718
}
9689
9719
9690
9720
function getBaseTypeVariableOfClass(symbol: Symbol) {
@@ -9772,12 +9802,17 @@ namespace ts {
9772
9802
9773
9803
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
9774
9804
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;
9781
9816
}
9782
9817
9783
9818
function reportCircularityError(symbol: Symbol) {
@@ -9820,23 +9855,36 @@ namespace ts {
9820
9855
}
9821
9856
9822
9857
/**
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.
9826
9864
*/
9827
9865
function getWriteTypeOfSymbol(symbol: Symbol): Type {
9828
9866
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
+ }
9835
9878
}
9879
+ return getSetAccessorTypeOfSymbol(symbol);
9880
+ }
9881
+
9882
+ function getSetAccessorTypeOfSymbol(symbol: Symbol): Type {
9836
9883
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
+ }
9840
9888
}
9841
9889
return getTypeOfSymbol(symbol);
9842
9890
}
@@ -25199,7 +25247,7 @@ namespace ts {
25199
25247
}
25200
25248
}
25201
25249
if (isDeclarationName(location) && isSetAccessor(location.parent) && getAnnotatedAccessorTypeNode(location.parent)) {
25202
- return getWriteTypeOfAccessors (location.parent.symbol) ;
25250
+ return resolveTypeOfAccessors (location.parent.symbol, /*writing*/ true)! ;
25203
25251
}
25204
25252
// The location isn't a reference to the given symbol, meaning we're being asked
25205
25253
// a hypothetical question of what type the symbol would have if there was a reference
0 commit comments