@@ -2006,6 +2006,9 @@ namespace ts {
20062006 if ( currentFlow && isNarrowableReference ( < Expression > node ) ) {
20072007 node . flowNode = currentFlow ;
20082008 }
2009+ if ( isSpecialPropertyDeclaration ( node as PropertyAccessExpression ) ) {
2010+ bindSpecialPropertyDeclaration ( node as PropertyAccessExpression ) ;
2011+ }
20092012 break ;
20102013 case SyntaxKind . BinaryExpression :
20112014 const specialKind = getSpecialPropertyAssignmentKind ( node as BinaryExpression ) ;
@@ -2314,7 +2317,7 @@ namespace ts {
23142317 declareSymbol ( file . symbol . exports , file . symbol , node , SymbolFlags . Property | SymbolFlags . ExportValue | SymbolFlags . ValueModule , SymbolFlags . None ) ;
23152318 }
23162319
2317- function bindThisPropertyAssignment ( node : BinaryExpression ) {
2320+ function bindThisPropertyAssignment ( node : BinaryExpression | PropertyAccessExpression ) {
23182321 Debug . assert ( isInJavaScriptFile ( node ) ) ;
23192322 const container = getThisContainer ( node , /*includeArrowFunctions*/ false ) ;
23202323 switch ( container . kind ) {
@@ -2340,8 +2343,19 @@ namespace ts {
23402343 }
23412344 }
23422345
2346+ function bindSpecialPropertyDeclaration ( node : PropertyAccessExpression ) {
2347+ Debug . assert ( isInJavaScriptFile ( node ) ) ;
2348+ if ( node . expression . kind === SyntaxKind . ThisKeyword ) {
2349+ bindThisPropertyAssignment ( node ) ;
2350+ }
2351+ else if ( ( node . expression . kind === SyntaxKind . Identifier || node . expression . kind === SyntaxKind . PropertyAccessExpression ) &&
2352+ node . parent . parent . kind === SyntaxKind . SourceFile ) {
2353+ bindStaticPropertyAssignment ( node ) ;
2354+ }
2355+ }
2356+
23432357 function bindPrototypePropertyAssignment ( node : BinaryExpression ) {
2344- // We saw a node of the form 'x.prototype.y = z'. Declare a 'member' y on x if x was a function.
2358+ // We saw a node of the form 'x.prototype.y = z'. Declare a 'member' y on x if x is a function or class, or not declared .
23452359
23462360 // Look up the function in the local scope, since prototype assignments should
23472361 // follow the function declaration
@@ -2357,24 +2371,27 @@ namespace ts {
23572371 bindPropertyAssignment ( constructorFunction . escapedText , leftSideOfAssignment , /*isPrototypeProperty*/ true ) ;
23582372 }
23592373
2360- function bindStaticPropertyAssignment ( node : BinaryExpression ) {
2361- // We saw a node of the form 'x.y = z'. Declare a 'member' y on x if x was a function.
2362-
2363- // Look up the function in the local scope, since prototype assignments should
2374+ /**
2375+ * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function or class, or not declared.
2376+ * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y;
2377+ */
2378+ function bindStaticPropertyAssignment ( node : BinaryExpression | PropertyAccessExpression ) {
2379+ // Look up the function in the local scope, since static assignments should
23642380 // follow the function declaration
2365- const leftSideOfAssignment = node . left as PropertyAccessExpression ;
2381+ const leftSideOfAssignment = node . kind === SyntaxKind . PropertyAccessExpression ? node : node . left as PropertyAccessExpression ;
23662382 const target = leftSideOfAssignment . expression ;
23672383
23682384 if ( isIdentifier ( target ) ) {
23692385 // Fix up parent pointers since we're going to use these nodes before we bind into them
2370- leftSideOfAssignment . parent = node ;
23712386 target . parent = leftSideOfAssignment ;
2372-
2387+ if ( node . kind === SyntaxKind . BinaryExpression ) {
2388+ leftSideOfAssignment . parent = node ;
2389+ }
23732390 if ( isNameOfExportsOrModuleExportsAliasDeclaration ( target ) ) {
23742391 // This can be an alias for the 'exports' or 'module.exports' names, e.g.
23752392 // var util = module.exports;
23762393 // util.property = function ...
2377- bindExportsPropertyAssignment ( node ) ;
2394+ bindExportsPropertyAssignment ( node as BinaryExpression ) ;
23782395 }
23792396 else {
23802397 bindPropertyAssignment ( target . escapedText , leftSideOfAssignment , /*isPrototypeProperty*/ false ) ;
@@ -2383,17 +2400,41 @@ namespace ts {
23832400 }
23842401
23852402 function lookupSymbolForName ( name : __String ) {
2386- return ( container . symbol && container . symbol . exports && container . symbol . exports . get ( name ) ) || ( container . locals && container . locals . get ( name ) ) ;
2403+ const local = container . locals && container . locals . get ( name ) ;
2404+ if ( local ) {
2405+ return local . exportSymbol || local ;
2406+ }
2407+ return container . symbol && container . symbol . exports && container . symbol . exports . get ( name ) ;
23872408 }
23882409
2389- function bindPropertyAssignment ( functionName : __String , propertyAccessExpression : PropertyAccessExpression , isPrototypeProperty : boolean ) {
2390- let targetSymbol = lookupSymbolForName ( functionName ) ;
2391-
2392- if ( targetSymbol && isDeclarationOfFunctionOrClassExpression ( targetSymbol ) ) {
2393- targetSymbol = ( targetSymbol . valueDeclaration as VariableDeclaration ) . initializer . symbol ;
2410+ function bindPropertyAssignment ( functionName : __String , propertyAccess : PropertyAccessExpression , isPrototypeProperty : boolean ) {
2411+ const symbol = lookupSymbolForName ( functionName ) ;
2412+ let targetSymbol = symbol && isDeclarationOfFunctionOrClassExpression ( symbol ) ?
2413+ ( symbol . valueDeclaration as VariableDeclaration ) . initializer . symbol :
2414+ symbol ;
2415+ Debug . assert ( propertyAccess . parent . kind === SyntaxKind . BinaryExpression || propertyAccess . parent . kind === SyntaxKind . ExpressionStatement ) ;
2416+ let isLegalPosition : boolean ;
2417+ if ( propertyAccess . parent . kind === SyntaxKind . BinaryExpression ) {
2418+ const initializerKind = ( propertyAccess . parent as BinaryExpression ) . right . kind ;
2419+ isLegalPosition = ( initializerKind === SyntaxKind . ClassExpression || initializerKind === SyntaxKind . FunctionExpression ) &&
2420+ propertyAccess . parent . parent . parent . kind === SyntaxKind . SourceFile ;
23942421 }
2395-
2396- if ( ! targetSymbol || ! ( targetSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Class ) ) ) {
2422+ else {
2423+ isLegalPosition = propertyAccess . parent . parent . kind === SyntaxKind . SourceFile ;
2424+ }
2425+ if ( ! isPrototypeProperty && ( ! targetSymbol || ! ( targetSymbol . flags & SymbolFlags . Namespace ) ) && isLegalPosition ) {
2426+ Debug . assert ( isIdentifier ( propertyAccess . expression ) ) ;
2427+ const identifier = propertyAccess . expression as Identifier ;
2428+ const flags = SymbolFlags . Module | SymbolFlags . JSContainer ;
2429+ const excludeFlags = SymbolFlags . ValueModuleExcludes & ~ SymbolFlags . JSContainer ;
2430+ if ( targetSymbol ) {
2431+ addDeclarationToSymbol ( symbol , identifier , flags ) ;
2432+ }
2433+ else {
2434+ targetSymbol = declareSymbol ( container . locals , /*parent*/ undefined , identifier , flags , excludeFlags ) ;
2435+ }
2436+ }
2437+ if ( ! targetSymbol || ! ( targetSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Class | SymbolFlags . NamespaceModule ) ) ) {
23972438 return ;
23982439 }
23992440
@@ -2403,7 +2444,7 @@ namespace ts {
24032444 ( targetSymbol . exports || ( targetSymbol . exports = createSymbolTable ( ) ) ) ;
24042445
24052446 // Declare the method/property
2406- declareSymbol ( symbolTable , targetSymbol , propertyAccessExpression , SymbolFlags . Property , SymbolFlags . PropertyExcludes ) ;
2447+ declareSymbol ( symbolTable , targetSymbol , propertyAccess , SymbolFlags . Property , SymbolFlags . PropertyExcludes ) ;
24072448 }
24082449
24092450 function bindCallExpression ( node : CallExpression ) {
0 commit comments