From 8bd66a095d60724cd8ba94cba8b11fbd06bb4930 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 29 Nov 2017 11:36:25 -0800 Subject: [PATCH] JS Object literal assignments are declarations Previously this only worked cross-file because it was a merge. Now it works anywhere, and locally it is actually binding a new property on the object literal symbol. --- src/compiler/binder.ts | 4 ++-- src/compiler/checker.ts | 4 ++++ src/compiler/utilities.ts | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index bbfbef52ff7..b00282b8a62 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2409,7 +2409,7 @@ namespace ts { function bindPropertyAssignment(functionName: __String, propertyAccess: PropertyAccessExpression, isPrototypeProperty: boolean) { const symbol = lookupSymbolForName(functionName); - let targetSymbol = symbol && isDeclarationOfFunctionOrClassExpression(symbol) ? + let targetSymbol = symbol && isDeclarationOfJavascriptExpression(symbol) ? (symbol.valueDeclaration as VariableDeclaration).initializer.symbol : symbol; Debug.assert(propertyAccess.parent.kind === SyntaxKind.BinaryExpression || propertyAccess.parent.kind === SyntaxKind.ExpressionStatement); @@ -2432,7 +2432,7 @@ namespace ts { targetSymbol = declareSymbol(container.locals, /*parent*/ undefined, identifier, SymbolFlags.Module, SymbolFlags.ValueModuleExcludes); } } - if (!targetSymbol || !(targetSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule))) { + if (!targetSymbol || !(targetSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule | SymbolFlags.ObjectLiteral))) { return; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ab2e2fbe27..8f0758a5fff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14431,6 +14431,10 @@ namespace ts { let hasComputedStringProperty = false; let hasComputedNumberProperty = false; const isInJSFile = isInJavaScriptFile(node); + if (isInJSFile && node.symbol && node.symbol.exports) { + mergeSymbolTable(propertiesTable, node.symbol.exports); + node.symbol.exports.forEach(symbol => propertiesArray.push(symbol)); + } let offset = 0; for (let i = 0; i < node.properties.length; i++) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 80ca3bbdfad..386906ae297 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1441,7 +1441,24 @@ namespace ts { export function isDeclarationOfFunctionOrClassExpression(s: Symbol) { if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) { const declaration = s.valueDeclaration as VariableDeclaration; - return declaration.initializer && (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression); + return declaration.initializer && + (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression); + } + return false; + } + + /** + * Returns true if the node is a variable declaration whose initializer is a function or class expression, or an empty object literal. + * This function does not test if the node is in a JavaScript file or not. + */ + export function isDeclarationOfJavascriptExpression(s: Symbol) { + if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) { + const declaration = s.valueDeclaration as VariableDeclaration; + return declaration.initializer && + (declaration.initializer.kind === SyntaxKind.FunctionExpression || + declaration.initializer.kind === SyntaxKind.ClassExpression || + (declaration.initializer.kind === SyntaxKind.ObjectLiteralExpression && + (declaration.initializer as ObjectLiteralExpression).properties.length === 0)); } return false; }