diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8491a9d9c52..866c282c3e3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a25e372a0aa..85d3bef4d4d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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 { diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.symbols b/tests/baselines/reference/typeFromPropertyAssignment9.symbols index 966d4d5a5c0..9af8fffdc88 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.symbols +++ b/tests/baselines/reference/typeFromPropertyAssignment9.symbols @@ -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)) + + diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.types b/tests/baselines/reference/typeFromPropertyAssignment9.types index 5d6c03f72b0..e57d8b70bf7 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.types +++ b/tests/baselines/reference/typeFromPropertyAssignment9.types @@ -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; } + + +