Support var x = x || {} declarations in JS

This commit is contained in:
Nathan Shively-Sanders
2018-02-07 11:26:30 -08:00
parent 4f07f58c03
commit 9e8d59ca14
3 changed files with 36 additions and 14 deletions
+2 -2
View File
@@ -2407,8 +2407,8 @@ namespace ts {
function bindPropertyAssignment(functionName: __String, propertyAccess: PropertyAccessExpression, isPrototypeProperty: boolean) {
const symbol = lookupSymbolForName(functionName);
let targetSymbol = symbol && isDeclarationOfJavascriptExpression(symbol) ?
(symbol.valueDeclaration as VariableDeclaration).initializer.symbol :
let targetSymbol = symbol && isDeclarationOfJavascriptContainerExpression(symbol) ? (symbol.valueDeclaration as VariableDeclaration).initializer.symbol :
symbol && isDeclarationOfDefaultedJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration as VariableDeclaration).initializer as BinaryExpression).right.symbol :
symbol;
Debug.assert(propertyAccess.parent.kind === SyntaxKind.BinaryExpression || propertyAccess.parent.kind === SyntaxKind.ExpressionStatement);
let isLegalPosition: boolean;
+11 -4
View File
@@ -4359,6 +4359,10 @@ namespace ts {
if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
}
if (isInJavaScriptFile(declaration) && isDeclarationOfDefaultedJavascriptContainerExpression(symbol)) {
// !!! (probably out of place, probably not the right function to call)
return links.type = checkExpression(((declaration as VariableDeclaration).initializer as BinaryExpression).right);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
@@ -4379,7 +4383,7 @@ namespace ts {
|| isIdentifier(declaration)
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
|| isMethodSignature(declaration)) {
// TODO: Might need to add a case here?
// Symbol is property of some kind that is merged with something - should use `getTypeOfFuncClassEnumModule` and not `getTypeOfVariableOrParameterOrProperty`
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
return getTypeOfFuncClassEnumModule(symbol);
@@ -14744,15 +14748,18 @@ namespace ts {
let spread: Type = emptyObjectType;
let propagatedFlags: TypeFlags = TypeFlags.FreshLiteral;
const contextualType = getApparentTypeOfContextualType(node);
const isInJSFile = isInJavaScriptFile(node);
// TODO: Might need to skip if isDeclarationOfDefaultedJavascriptContainerExpression(node.parent.parent) instead of just checking node.properties.length > 0
// (actually, it would be better not to skip contextual typing at all, but to do that I need to avoid the checking loop another way)
const contextualType = node.properties.length > 0 && getApparentTypeOfContextualType(node);
const contextualTypeHasPattern = contextualType && contextualType.pattern &&
(contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression);
const isJSObjectLiteral = !contextualType && isInJavaScriptFile(node);
const isJSObjectLiteral = !contextualType && isInJSFile;
let typeFlags: TypeFlags = 0;
let patternWithComputedProperties = false;
let hasComputedStringProperty = false;
let hasComputedNumberProperty = false;
const isInJSFile = isInJavaScriptFile(node);
// TODO: This seems like the wrong way to put the assignment-properties into the type (especially the manual call to mergeSymbolTable)
if (isInJSFile && node.symbol && node.symbol.exports) {
mergeSymbolTable(propertiesTable, node.symbol.exports);
node.symbol.exports.forEach(symbol => propertiesArray.push(symbol));
+23 -8
View File
@@ -1489,14 +1489,29 @@ namespace ts {
* 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));
export function isDeclarationOfJavascriptContainerExpression(s: Symbol) {
if (s.valueDeclaration && isVariableDeclaration(s.valueDeclaration)) {
return isVariableDeclaration(s.valueDeclaration) && isJavascriptContainerExpression(s.valueDeclaration.initializer);
}
return false;
}
export function isJavascriptContainerExpression(expression: Expression | undefined) {
return expression &&
(expression.kind === SyntaxKind.FunctionExpression ||
expression.kind === SyntaxKind.ClassExpression ||
isObjectLiteralExpression(expression) && expression.properties.length === 0)
}
export function isDeclarationOfDefaultedJavascriptContainerExpression(s: Symbol) {
if (s.valueDeclaration && isVariableDeclaration(s.valueDeclaration)) {
return s.valueDeclaration.initializer &&
isBinaryExpression(s.valueDeclaration.initializer) &&
isIdentifier(s.valueDeclaration.initializer.left) &&
isIdentifier(s.valueDeclaration.name) &&
s.valueDeclaration.initializer.left.escapedText === s.valueDeclaration.name.escapedText &&
isObjectLiteralExpression(s.valueDeclaration.initializer.right) &&
s.valueDeclaration.initializer.right.properties.length === 0;
}
return false;
}