Support o.x = o.x || {} assignments in JS

Plus add tests for them
This commit is contained in:
Nathan Shively-Sanders
2018-02-07 14:54:34 -08:00
parent a51bce0ab5
commit 2f8c237270
4 changed files with 98 additions and 45 deletions
+4
View File
@@ -4363,6 +4363,10 @@ namespace ts {
// !!! (probably out of place, probably not the right function to call)
return links.type = checkExpression(((declaration as VariableDeclaration).initializer as BinaryExpression).right);
}
if (isInJavaScriptFile(declaration) && isAssignmentOfDefaultedJavascriptContainerExpression(symbol)) {
// !!! (probably out of place, probably not the right function to call)
return links.type = checkExpression(((declaration.parent as BinaryExpression).right as BinaryExpression).right);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
+26 -10
View File
@@ -1504,16 +1504,32 @@ namespace ts {
}
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;
return s.valueDeclaration &&
isVariableDeclaration(s.valueDeclaration) &&
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 &&
// TODO: Should still probably be isJavascriptContainerExpression
isObjectLiteralExpression(s.valueDeclaration.initializer.right) &&
s.valueDeclaration.initializer.right.properties.length === 0;
}
export function isAssignmentOfDefaultedJavascriptContainerExpression(s: Symbol) {
return s.valueDeclaration &&
isPropertyAccessExpression(s.valueDeclaration) &&
isBinaryExpression(s.valueDeclaration.parent) &&
s.valueDeclaration.parent.right &&
isBinaryExpression(s.valueDeclaration.parent.right) &&
isPropertyAccessExpression(s.valueDeclaration.parent.right.left) &&
isIdentifier(s.valueDeclaration.parent.right.left.expression) &&
isPropertyAccessExpression(s.valueDeclaration.parent.left) &&
isIdentifier(s.valueDeclaration.parent.left.expression) &&
s.valueDeclaration.parent.right.left.expression.escapedText === s.valueDeclaration.parent.left.expression.escapedText &&
s.valueDeclaration.parent.right.left.name.escapedText === s.valueDeclaration.parent.left.name.escapedText &&
isObjectLiteralExpression(s.valueDeclaration.parent.right.right) &&
s.valueDeclaration.parent.right.right.properties.length === 0;
}
export function getRightMostAssignedExpression(node: Expression): Expression {
@@ -12,28 +12,43 @@ my.m = function() {
return 1;
}
my.p = {};
>my.p : Symbol(p, Decl(a.js, 5, 1))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>p : Symbol(p, Decl(a.js, 5, 1))
my.n = 1;
>my.n : Symbol(n, Decl(a.js, 6, 10))
>my.n : Symbol(n, Decl(a.js, 5, 1))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>n : Symbol(n, Decl(a.js, 6, 10))
>n : Symbol(n, Decl(a.js, 5, 1))
my.m
my.o = {};
>my.o : Symbol(o, Decl(a.js, 6, 9))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>o : Symbol(o, Decl(a.js, 6, 9))
my.possible = my.possible || {};
>my.possible : Symbol(possible, Decl(a.js, 7, 10))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>possible : Symbol(possible, Decl(a.js, 7, 10))
>my.possible : Symbol(possible, Decl(a.js, 7, 10))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>possible : Symbol(possible, Decl(a.js, 7, 10))
my.m;
>my.m : Symbol(m, Decl(a.js, 2, 18))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>m : Symbol(m, Decl(a.js, 2, 18))
my.p
>my.p : Symbol(p, Decl(a.js, 5, 1))
my.o;
>my.o : Symbol(o, Decl(a.js, 6, 9))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>p : Symbol(p, Decl(a.js, 5, 1))
>o : Symbol(o, Decl(a.js, 6, 9))
my.n
>my.n : Symbol(n, Decl(a.js, 6, 10))
my.n;
>my.n : Symbol(n, Decl(a.js, 5, 1))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>n : Symbol(n, Decl(a.js, 6, 10))
>n : Symbol(n, Decl(a.js, 5, 1))
my.possible;
>my.possible : Symbol(possible, Decl(a.js, 7, 10))
>my : Symbol(my, Decl(a.js, 2, 3), Decl(a.js, 2, 18))
>possible : Symbol(possible, Decl(a.js, 7, 10))
@@ -2,47 +2,65 @@
// TODO: JSDoc would provide a contextual type, so ... I should test that
// TODO: Try initializer of function or class I guess (though classes aren't context sensitive)
var my = my || {};
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my || {} : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>{} : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>my || {} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>{} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
my.m = function() {
>my.m = function() { return 1;} : () => number
>my.m : () => number
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>m : () => number
>function() { return 1;} : () => number
return 1;
>1 : 1
}
my.p = {};
>my.p = {} : { [x: string]: any; }
>my.p : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>p : { [x: string]: any; }
>{} : { [x: string]: any; }
my.n = 1;
>my.n = 1 : 1
>my.n : number
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>n : number
>1 : 1
my.m
my.o = {};
>my.o = {} : { [x: string]: any; }
>my.o : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>o : { [x: string]: any; }
>{} : { [x: string]: any; }
my.possible = my.possible || {};
>my.possible = my.possible || {} : { [x: string]: any; }
>my.possible : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>possible : { [x: string]: any; }
>my.possible || {} : { [x: string]: any; }
>my.possible : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>possible : { [x: string]: any; }
>{} : { [x: string]: any; }
my.m;
>my.m : () => number
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>m : () => number
my.p
>my.p : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>p : { [x: string]: any; }
my.o;
>my.o : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>o : { [x: string]: any; }
my.n
my.n;
>my.n : number
>my : { [x: string]: any; m: () => number; p: { [x: string]: any; }; n: number; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>n : number
my.possible;
>my.possible : { [x: string]: any; }
>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; possible: { [x: string]: any; }; }
>possible : { [x: string]: any; }