@@ -581,6 +581,8 @@ type Checker struct {
581
581
templateLiteralTypes map[string]*Type
582
582
stringMappingTypes map[StringMappingKey]*Type
583
583
uniqueESSymbolTypes map[*ast.Symbol]*Type
584
+ thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind
585
+ thisExpandoLocations map[*ast.Symbol]*ast.Node
584
586
subtypeReductionCache map[string][]*Type
585
587
cachedTypes map[CachedTypeKey]*Type
586
588
cachedSignatures map[CachedSignatureKey]*Signature
@@ -866,6 +868,8 @@ func NewChecker(program Program) *Checker {
866
868
c.templateLiteralTypes = make(map[string]*Type)
867
869
c.stringMappingTypes = make(map[StringMappingKey]*Type)
868
870
c.uniqueESSymbolTypes = make(map[*ast.Symbol]*Type)
871
+ c.thisExpandoKinds = make(map[*ast.Symbol]thisAssignmentDeclarationKind)
872
+ c.thisExpandoLocations = make(map[*ast.Symbol]*ast.Node)
869
873
c.subtypeReductionCache = make(map[string][]*Type)
870
874
c.cachedTypes = make(map[CachedTypeKey]*Type)
871
875
c.cachedSignatures = make(map[CachedSignatureKey]*Signature)
@@ -10809,9 +10813,9 @@ func (c *Checker) getFlowTypeOfProperty(reference *ast.Node, prop *ast.Symbol) *
10809
10813
func (c *Checker) getTypeOfPropertyInBaseClass(property *ast.Symbol) *Type {
10810
10814
classType := c.getDeclaringClass(property)
10811
10815
if classType != nil {
10812
- baseClassType := c.getBaseTypes(classType)[0]
10813
- if baseClassType != nil {
10814
- return c.getTypeOfPropertyOfType(baseClassType , property.Name)
10816
+ baseClassTypes := c.getBaseTypes(classType)
10817
+ if len(baseClassTypes) > 0 {
10818
+ return c.getTypeOfPropertyOfType(baseClassTypes[0] , property.Name)
10815
10819
}
10816
10820
}
10817
10821
return nil
@@ -16710,14 +16714,101 @@ func (c *Checker) getTypeOfPrototypeProperty(prototype *ast.Symbol) *Type {
16710
16714
return classType
16711
16715
}
16712
16716
16717
+ type thisAssignmentDeclarationKind int32
16718
+
16719
+ const (
16720
+ thisAssignmentDeclarationNone thisAssignmentDeclarationKind = iota // not (all) this.property assignments
16721
+ thisAssignmentDeclarationTyped // typed; use the type annotation
16722
+ thisAssignmentDeclarationConstructor // at least one in the constructor; use control flow
16723
+ thisAssignmentDeclarationMethod // methods only; look in base first, and if not found, union all declaration types plus undefined
16724
+ )
16725
+
16713
16726
func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *ast.Symbol) *Type {
16714
- var types []*Type
16727
+ var t *Type
16728
+ kind, location := c.isConstructorDeclaredThisProperty(symbol)
16729
+ if kind == thisAssignmentDeclarationTyped {
16730
+ if location == nil {
16731
+ panic("location should not be nil when this assignment has a type.")
16732
+ }
16733
+ t = c.getTypeFromTypeNode(location)
16734
+ } else if kind == thisAssignmentDeclarationConstructor {
16735
+ if location == nil {
16736
+ panic("constructor should not be nil when this assignment is in a constructor.")
16737
+ }
16738
+ t = c.getFlowTypeInConstructor(symbol, location)
16739
+ } else if kind == thisAssignmentDeclarationMethod {
16740
+ t = c.getTypeOfPropertyInBaseClass(symbol)
16741
+ }
16742
+ if t == nil {
16743
+ var types []*Type
16744
+ for _, declaration := range symbol.Declarations {
16745
+ if ast.IsBinaryExpression(declaration) {
16746
+ types = core.AppendIfUnique(types, c.checkExpressionForMutableLocation(declaration.AsBinaryExpression().Right, CheckModeNormal))
16747
+ }
16748
+ }
16749
+ if kind == thisAssignmentDeclarationMethod && len(types) > 0 {
16750
+ if c.strictNullChecks {
16751
+ types = core.AppendIfUnique(types, c.undefinedOrMissingType)
16752
+ }
16753
+ }
16754
+ t = c.getWidenedType(c.getUnionType(types))
16755
+ }
16756
+ // report an all-nullable or empty union as an implicit any in JS files
16757
+ if symbol.ValueDeclaration != nil && ast.IsInJSFile(symbol.ValueDeclaration) &&
16758
+ c.filterType(t, func(c *Type) bool { return c.Flags() & ^TypeFlagsNullable != 0 }) == c.neverType {
16759
+ c.reportImplicitAny(symbol.ValueDeclaration, c.anyType, WideningKindNormal)
16760
+ return c.anyType
16761
+ }
16762
+ return t
16763
+ }
16764
+
16765
+ // A property is considered a constructor declared property when all declaration sites are this.xxx assignments,
16766
+ // when no declaration sites have JSDoc type annotations, and when at least one declaration site is in the body of
16767
+ // a class constructor.
16768
+ func (c *Checker) isConstructorDeclaredThisProperty(symbol *ast.Symbol) (thisAssignmentDeclarationKind, *ast.Node) {
16769
+ if symbol.ValueDeclaration == nil || !ast.IsBinaryExpression(symbol.ValueDeclaration) {
16770
+ return thisAssignmentDeclarationNone, nil
16771
+ }
16772
+ if kind, ok := c.thisExpandoKinds[symbol]; ok {
16773
+ location, ok2 := c.thisExpandoLocations[symbol]
16774
+ if !ok2 {
16775
+ panic("ctor should be cached whenever this expando location is cached")
16776
+ }
16777
+ return kind, location
16778
+ }
16779
+ allThis := true
16780
+ var typeAnnotation *ast.Node
16715
16781
for _, declaration := range symbol.Declarations {
16716
- if ast.IsBinaryExpression(declaration) {
16717
- types = core.AppendIfUnique(types, c.checkExpressionForMutableLocation(declaration.AsBinaryExpression().Right, CheckModeNormal))
16782
+ if !ast.IsBinaryExpression(declaration) {
16783
+ allThis = false
16784
+ break
16785
+ }
16786
+ bin := declaration.AsBinaryExpression()
16787
+ if ast.GetAssignmentDeclarationKind(bin) == ast.JSDeclarationKindThisProperty &&
16788
+ (bin.Left.Kind != ast.KindElementAccessExpression || ast.IsStringOrNumericLiteralLike(bin.Left.AsElementAccessExpression().ArgumentExpression)) {
16789
+ // TODO: if bin.Type() != nil, use bin.Type()
16790
+ if bin.Right.Kind == ast.KindTypeAssertionExpression {
16791
+ typeAnnotation = bin.Right.AsTypeAssertion().Type
16792
+ }
16793
+ } else {
16794
+ allThis = false
16795
+ break
16718
16796
}
16719
16797
}
16720
- return c.getWidenedType(c.getUnionType(types))
16798
+ var location *ast.Node
16799
+ kind := thisAssignmentDeclarationNone
16800
+ if allThis {
16801
+ if typeAnnotation != nil {
16802
+ location = typeAnnotation
16803
+ kind = thisAssignmentDeclarationTyped
16804
+ } else {
16805
+ location = c.getDeclaringConstructor(symbol)
16806
+ kind = core.IfElse(location == nil, thisAssignmentDeclarationMethod, thisAssignmentDeclarationConstructor)
16807
+ }
16808
+ }
16809
+ c.thisExpandoKinds[symbol] = kind
16810
+ c.thisExpandoLocations[symbol] = location
16811
+ return kind, location
16721
16812
}
16722
16813
16723
16814
func (c *Checker) widenTypeForVariableLikeDeclaration(t *Type, declaration *ast.Node, reportErrors bool) *Type {
@@ -27595,8 +27686,8 @@ func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags
27595
27686
case ast.KindEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindBarBarEqualsToken, ast.KindQuestionQuestionEqualsToken:
27596
27687
// In an assignment expression, the right operand is contextually typed by the type of the left operand
27597
27688
// unless it's an assignment declaration.
27598
- if node == binary.Right && !c.isReferenceToModuleExports(binary.Left) && (binary.Symbol == nil || c.canGetContextualTypeForAssignmentDeclaration(binary.Left )) {
27599
- return c.getContextualTypeFromAssignmentTarget (binary.Left)
27689
+ if node == binary.Right && !c.isReferenceToModuleExports(binary.Left) && (binary.Symbol == nil || c.canGetContextualTypeForAssignmentDeclaration(node.Parent )) {
27690
+ return c.getTypeOfExpression (binary.Left)
27600
27691
}
27601
27692
case ast.KindBarBarToken, ast.KindQuestionQuestionToken:
27602
27693
// When an || expression has a contextual type, the operands are contextually typed by that type, except
@@ -27622,47 +27713,60 @@ func (c *Checker) canGetContextualTypeForAssignmentDeclaration(node *ast.Node) b
27622
27713
// binder) of the form 'F.id = expr' or 'F[xxx] = expr'. If 'F' is declared as a variable with a type annotation,
27623
27714
// we can obtain a contextual type from the annotated type without triggering a circularity. Otherwise, the
27624
27715
// assignment declaration has no contextual type.
27625
- symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(node.Expression()))
27626
- return symbol.ValueDeclaration != nil && ast.IsVariableDeclaration(symbol.ValueDeclaration) && symbol.ValueDeclaration.Type() != nil
27627
- }
27628
-
27629
- func (c *Checker) isReferenceToModuleExports(node *ast.Node) bool {
27630
- if ast.IsAccessExpression(node) {
27631
- expr := node.Expression()
27632
- if ast.IsIdentifier(expr) {
27633
- // Node is the left operand of an assignment expression of the form 'module.exports = expr'.
27634
- symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(expr))
27635
- return symbol.Flags&ast.SymbolFlagsModuleExports != 0
27636
- }
27637
- }
27638
- return false
27639
- }
27640
-
27641
- func (c *Checker) getContextualTypeFromAssignmentTarget(node *ast.Node) *Type {
27642
- if ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindThisKeyword {
27716
+ left := node.AsBinaryExpression().Left
27717
+ expr := left.Expression()
27718
+ if ast.IsAccessExpression(left) && expr.Kind == ast.KindThisKeyword {
27643
27719
var symbol *ast.Symbol
27644
- if ast.IsPropertyAccessExpression(node ) {
27645
- name := node .Name()
27646
- thisType := c.getTypeOfExpression(node.Expression() )
27720
+ if ast.IsPropertyAccessExpression(left ) {
27721
+ name := left .Name()
27722
+ thisType := c.getTypeOfExpression(expr )
27647
27723
if ast.IsPrivateIdentifier(name) {
27648
27724
symbol = c.getPropertyOfType(thisType, binder.GetSymbolNameForPrivateIdentifier(thisType.symbol, name.Text()))
27649
27725
} else {
27650
27726
symbol = c.getPropertyOfType(thisType, name.Text())
27651
27727
}
27652
27728
} else {
27653
- propType := c.checkExpressionCached(node .AsElementAccessExpression().ArgumentExpression)
27729
+ propType := c.checkExpressionCached(left .AsElementAccessExpression().ArgumentExpression)
27654
27730
if isTypeUsableAsPropertyName(propType) {
27655
- symbol = c.getPropertyOfType(c.getTypeOfExpression(node.Expression() ), getPropertyNameFromType(propType))
27731
+ symbol = c.getPropertyOfType(c.getTypeOfExpression(expr ), getPropertyNameFromType(propType))
27656
27732
}
27657
27733
}
27658
27734
if symbol != nil {
27659
27735
d := symbol.ValueDeclaration
27660
27736
if d != nil && (ast.IsPropertyDeclaration(d) || ast.IsPropertySignatureDeclaration(d)) && d.Type() == nil && d.Initializer() == nil {
27661
- return nil
27737
+ return false
27738
+ }
27739
+ }
27740
+ symbol = node.Symbol()
27741
+ if symbol != nil && symbol.ValueDeclaration != nil && symbol.ValueDeclaration.Type() == nil {
27742
+ if !ast.IsObjectLiteralMethod(c.getThisContainer(expr, false, false)) {
27743
+ return false
27744
+ }
27745
+ // and now for one single case of object literal methods
27746
+ name := ast.GetElementOrPropertyAccessArgumentExpressionOrName(left)
27747
+ if name == nil {
27748
+ return false
27749
+ } else {
27750
+ // !!! contextual typing for `this` in object literals
27751
+ return false
27662
27752
}
27663
27753
}
27754
+ return true
27664
27755
}
27665
- return c.getTypeOfExpression(node)
27756
+ symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(expr))
27757
+ return symbol.ValueDeclaration != nil && ast.IsVariableDeclaration(symbol.ValueDeclaration) && symbol.ValueDeclaration.Type() != nil
27758
+ }
27759
+
27760
+ func (c *Checker) isReferenceToModuleExports(node *ast.Node) bool {
27761
+ if ast.IsAccessExpression(node) {
27762
+ expr := node.Expression()
27763
+ if ast.IsIdentifier(expr) {
27764
+ // Node is the left operand of an assignment expression of the form 'module.exports = expr'.
27765
+ symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(expr))
27766
+ return symbol.Flags&ast.SymbolFlagsModuleExports != 0
27767
+ }
27768
+ }
27769
+ return false
27666
27770
}
27667
27771
27668
27772
func (c *Checker) getContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type {
0 commit comments