diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9d222994f2d..9b27b202748 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18984,6 +18984,9 @@ namespace ts { } function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) { + if (node.operatorToken.kind === SyntaxKind.BarBarToken && isInJavaScriptFile(node) && isAssignmentOfDefaultedJavascriptContainerExpression(node.parent)) { + return checkExpression(node.right, checkMode); + } return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node); } @@ -21859,7 +21862,8 @@ namespace ts { // Node is the primary declaration of the symbol, just validate the initializer // Don't validate for-in initializer as it is already an error if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) { - checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined); + const initializer = isInJavaScriptFile(node) && isDeclarationOfDefaultedJavascriptContainerExpression(node) ? (node.initializer as BinaryExpression).right : node.initializer; + checkTypeAssignableTo(checkExpressionCached(initializer), type, node, /*headMessage*/ undefined); checkParameterInitializer(node); } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7373b6c0591..80266cd78f6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1508,10 +1508,8 @@ namespace ts { isVariableDeclaration(node) && node.initializer && isBinaryExpression(node.initializer) && - // TODO: This will have to change for `var my = window.my || {}`; - isIdentifier(node.initializer.left) && isIdentifier(node.name) && - node.initializer.left.escapedText === node.name.escapedText && + isSameName(node.name, node.initializer.left as EntityNameExpression) && isJavascriptContainerExpression(node.initializer.right); } @@ -1534,12 +1532,20 @@ namespace ts { isJavascriptContainerExpression(node.right.right); } - function isSameName(left: EntityNameExpression, right: EntityNameExpression): boolean { - if (isIdentifier(left) && isIdentifier(right)) { - return left.escapedText === right.escapedText; + function isSameName(name: EntityNameExpression, initializer: EntityNameExpression): boolean { + if (isIdentifier(name) && isIdentifier(initializer)) { + return name.escapedText === initializer.escapedText; } - if (isPropertyAccessExpression(left) && isPropertyAccessExpression(right)) { - return left.name.escapedText === right.name.escapedText && isSameName(left.expression, right.expression); + if (isIdentifier(name) && isPropertyAccessExpression(initializer)) { + return (initializer.expression.kind as SyntaxKind.ThisKeyword === SyntaxKind.ThisKeyword || + isIdentifier(initializer.expression) && + (initializer.expression.escapedText === "window" as __String || + initializer.expression.escapedText === "self" as __String || + initializer.expression.escapedText === "global" as __String)) && + isSameName(name, initializer.name); + } + if (isPropertyAccessExpression(name) && isPropertyAccessExpression(initializer)) { + return name.name.escapedText === initializer.name.escapedText && isSameName(name.expression, initializer.expression); } return false; } @@ -1596,7 +1602,7 @@ namespace ts { return SpecialPropertyAssignmentKind.PrototypeProperty; } } - if(isEntityNameExpression(lhs.expression)) { + if (isEntityNameExpression(lhs.expression)) { return SpecialPropertyAssignmentKind.Property; } } diff --git a/tests/baselines/reference/typeFromPropertyAssignment8.symbols b/tests/baselines/reference/typeFromPropertyAssignment8.symbols new file mode 100644 index 00000000000..14598448b50 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment8.symbols @@ -0,0 +1,75 @@ +=== tests/cases/conformance/salsa/a.js === +var my = my || {}; +>my : Symbol(my, Decl(a.js, 0, 3)) +>my : Symbol(my, Decl(a.js, 0, 3)) + +my.app = my.app || {}; +>my.app : Symbol(app, Decl(a.js, 0, 18)) +>my : Symbol(my, Decl(a.js, 0, 3)) +>app : Symbol(app, Decl(a.js, 0, 18)) +>my.app : Symbol(app, Decl(a.js, 0, 18)) +>my : Symbol(my, Decl(a.js, 0, 3)) +>app : Symbol(app, Decl(a.js, 0, 18)) + +my.app.Application = (function () { +>my.app.Application : Symbol(Application, Decl(a.js, 1, 22)) +>my.app : Symbol(app, Decl(a.js, 0, 18)) +>my : Symbol(my, Decl(a.js, 0, 3)) +>app : Symbol(app, Decl(a.js, 0, 18)) +>Application : Symbol(Application, Decl(a.js, 1, 22)) + +var Application = function () { +>Application : Symbol(Application, Decl(a.js, 4, 3)) + + //... +}; +return Application; +>Application : Symbol(Application, Decl(a.js, 4, 3)) + +})(); +my.app.Application() +>my.app.Application : Symbol(Application, Decl(a.js, 1, 22)) +>my.app : Symbol(app, Decl(a.js, 0, 18)) +>my : Symbol(my, Decl(a.js, 0, 3)) +>app : Symbol(app, Decl(a.js, 0, 18)) +>Application : Symbol(Application, Decl(a.js, 1, 22)) + +=== tests/cases/conformance/salsa/b.js === +var min = window.min || {}; +>min : Symbol(min, Decl(b.js, 0, 3)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + +min.app = min.app || {}; +>min.app : Symbol(app, Decl(b.js, 0, 27)) +>min : Symbol(min, Decl(b.js, 0, 3)) +>app : Symbol(app, Decl(b.js, 0, 27)) +>min.app : Symbol(app, Decl(b.js, 0, 27)) +>min : Symbol(min, Decl(b.js, 0, 3)) +>app : Symbol(app, Decl(b.js, 0, 27)) + +min.app.Application = (function () { +>min.app.Application : Symbol(Application, Decl(b.js, 1, 24)) +>min.app : Symbol(app, Decl(b.js, 0, 27)) +>min : Symbol(min, Decl(b.js, 0, 3)) +>app : Symbol(app, Decl(b.js, 0, 27)) +>Application : Symbol(Application, Decl(b.js, 1, 24)) + +var Application = function () { +>Application : Symbol(Application, Decl(b.js, 4, 3)) + + //... +}; +return Application; +>Application : Symbol(Application, Decl(b.js, 4, 3)) + +})(); +min.app.Application() +>min.app.Application : Symbol(Application, Decl(b.js, 1, 24)) +>min.app : Symbol(app, Decl(b.js, 0, 27)) +>min : Symbol(min, Decl(b.js, 0, 3)) +>app : Symbol(app, Decl(b.js, 0, 27)) +>Application : Symbol(Application, Decl(b.js, 1, 24)) + +// TODO: Make sure to include github.com/chadbr/vscodeTestJS +// (though extracted to 4 js files instead of 2 js files and an html) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment8.types b/tests/baselines/reference/typeFromPropertyAssignment8.types new file mode 100644 index 00000000000..87b4db81405 --- /dev/null +++ b/tests/baselines/reference/typeFromPropertyAssignment8.types @@ -0,0 +1,99 @@ +=== tests/cases/conformance/salsa/a.js === +var my = my || {}; +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>my || {} : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>{} : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } + +my.app = my.app || {}; +>my.app = my.app || {} : { [x: string]: any; Application: () => void; } +>my.app : { [x: string]: any; Application: () => void; } +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>my.app || {} : { [x: string]: any; Application: () => void; } +>my.app : { [x: string]: any; Application: () => void; } +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>{} : { [x: string]: any; Application: () => void; } + +my.app.Application = (function () { +>my.app.Application = (function () {var Application = function () { //...};return Application;})() : () => void +>my.app.Application : () => void +>my.app : { [x: string]: any; Application: () => void; } +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>Application : () => void +>(function () {var Application = function () { //...};return Application;})() : () => void +>(function () {var Application = function () { //...};return Application;}) : () => () => void +>function () {var Application = function () { //...};return Application;} : () => () => void + +var Application = function () { +>Application : () => void +>function () { //...} : () => void + + //... +}; +return Application; +>Application : () => void + +})(); +my.app.Application() +>my.app.Application() : void +>my.app.Application : () => void +>my.app : { [x: string]: any; Application: () => void; } +>my : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>Application : () => void + +=== tests/cases/conformance/salsa/b.js === +var min = window.min || {}; +>min : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>window.min || {} : any +>window.min : any +>window : Window +>min : any +>{} : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } + +min.app = min.app || {}; +>min.app = min.app || {} : { [x: string]: any; Application: () => void; } +>min.app : { [x: string]: any; Application: () => void; } +>min : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>min.app || {} : { [x: string]: any; Application: () => void; } +>min.app : { [x: string]: any; Application: () => void; } +>min : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>{} : { [x: string]: any; Application: () => void; } + +min.app.Application = (function () { +>min.app.Application = (function () {var Application = function () { //...};return Application;})() : () => void +>min.app.Application : () => void +>min.app : { [x: string]: any; Application: () => void; } +>min : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>Application : () => void +>(function () {var Application = function () { //...};return Application;})() : () => void +>(function () {var Application = function () { //...};return Application;}) : () => () => void +>function () {var Application = function () { //...};return Application;} : () => () => void + +var Application = function () { +>Application : () => void +>function () { //...} : () => void + + //... +}; +return Application; +>Application : () => void + +})(); +min.app.Application() +>min.app.Application() : void +>min.app.Application : () => void +>min.app : { [x: string]: any; Application: () => void; } +>min : { [x: string]: any; app: { [x: string]: any; Application: () => void; }; } +>app : { [x: string]: any; Application: () => void; } +>Application : () => void + +// TODO: Make sure to include github.com/chadbr/vscodeTestJS +// (though extracted to 4 js files instead of 2 js files and an html) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.symbols b/tests/baselines/reference/typeFromPropertyAssignment9.symbols index 2c479f222db..d29cebf1eb2 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.symbols +++ b/tests/baselines/reference/typeFromPropertyAssignment9.symbols @@ -1,84 +1,113 @@ === tests/cases/conformance/salsa/a.js === // TODO: JSDoc would provide a contextual type, so ... I should test that -// (a number of existing tests fail because of that, I think) -// TODO: Try initializer of function or class I guess (though classes aren't context sensitive) -// TODO: Duplicated declarations should be OK (if they have the same type (??)) var my = my || {}; ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) -my.m = function() { ->my.m : Symbol(m, Decl(a.js, 4, 18)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->m : Symbol(m, Decl(a.js, 4, 18)) +my.method = function() { +>my.method : Symbol(method, Decl(a.js, 1, 18)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>method : Symbol(method, Decl(a.js, 1, 18)) return 1; } -my.n = 1; ->my.n : Symbol(n, Decl(a.js, 7, 1)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->n : Symbol(n, Decl(a.js, 7, 1)) +my.number = 1; +>my.number : Symbol(number, Decl(a.js, 4, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>number : Symbol(number, Decl(a.js, 4, 1)) -my.o = {}; ->my.o : Symbol(o, Decl(a.js, 8, 9)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->o : Symbol(o, Decl(a.js, 8, 9)) +my.object = {}; +>my.object : Symbol(object, Decl(a.js, 5, 14)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>object : Symbol(object, Decl(a.js, 5, 14)) my.predicate = my.predicate || {}; ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) my.predicate.query = function () { ->my.predicate.query : Symbol(query, Decl(a.js, 10, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->query : Symbol(query, Decl(a.js, 10, 34)) +>my.predicate.query : Symbol(query, Decl(a.js, 7, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>query : Symbol(query, Decl(a.js, 7, 34)) var me = this; ->me : Symbol(me, Decl(a.js, 12, 7)) ->this : Symbol(__object, Decl(a.js, 10, 30)) +>me : Symbol(me, Decl(a.js, 9, 7)) +>this : Symbol(__object, Decl(a.js, 7, 30)) me.property = false; ->me : Symbol(me, Decl(a.js, 12, 7)) +>me : Symbol(me, Decl(a.js, 9, 7)) }; var q = new my.predicate.query(); ->q : Symbol(q, Decl(a.js, 15, 3)) ->my.predicate.query : Symbol(query, Decl(a.js, 10, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->query : Symbol(query, Decl(a.js, 10, 34)) +>q : Symbol(q, Decl(a.js, 12, 3)) +>my.predicate.query : Symbol(query, Decl(a.js, 7, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>query : Symbol(query, Decl(a.js, 7, 34)) my.predicate.query.result = 'none' ->my.predicate.query.result : Symbol((Anonymous function).result, Decl(a.js, 15, 33)) ->my.predicate.query : Symbol(query, Decl(a.js, 10, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->query : Symbol(query, Decl(a.js, 10, 34)) ->result : Symbol((Anonymous function).result, Decl(a.js, 15, 33)) +>my.predicate.query.result : Symbol((Anonymous function).result, Decl(a.js, 12, 33)) +>my.predicate.query : Symbol(query, Decl(a.js, 7, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>query : Symbol(query, Decl(a.js, 7, 34)) +>result : Symbol((Anonymous function).result, Decl(a.js, 12, 33)) my.predicate.sort = my.predicate.sort || function (first, second) { ->my.predicate.sort : Symbol(sort, Decl(a.js, 16, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->sort : Symbol(sort, Decl(a.js, 16, 34)) ->my.predicate.sort : Symbol(sort, Decl(a.js, 16, 34)) ->my.predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->my : Symbol(my, Decl(a.js, 4, 3), Decl(a.js, 4, 18)) ->predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) ->sort : Symbol(sort, Decl(a.js, 16, 34)) ->first : Symbol(first, Decl(a.js, 17, 51)) ->second : Symbol(second, Decl(a.js, 17, 57)) +>my.predicate.sort : Symbol(sort, Decl(a.js, 13, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>sort : Symbol(sort, Decl(a.js, 13, 34)) +>my.predicate.sort : Symbol(sort, Decl(a.js, 13, 34)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>sort : Symbol(sort, Decl(a.js, 13, 34)) +>first : Symbol(first, Decl(a.js, 14, 51)) +>second : Symbol(second, Decl(a.js, 14, 57)) return first; ->first : Symbol(first, Decl(a.js, 17, 51)) +>first : Symbol(first, Decl(a.js, 14, 51)) +} +my.predicate.type = class { +>my.predicate.type : Symbol(type, Decl(a.js, 16, 1)) +>my.predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>my : Symbol(my, Decl(a.js, 1, 3), Decl(a.js, 1, 18)) +>predicate : Symbol(predicate, Decl(a.js, 6, 15), Decl(a.js, 7, 34), Decl(a.js, 16, 1)) +>type : Symbol(type, Decl(a.js, 16, 1)) + + m() { return 101; } +>m : Symbol((Anonymous class).m, Decl(a.js, 17, 27)) } + +// global-ish prefixes +var min = window.min || {}; +>min : Symbol(min, Decl(a.js, 23, 3)) + +min.nest = this.min.nest || function () { }; +>min.nest : Symbol(nest, Decl(a.js, 23, 27)) +>min : Symbol(min, Decl(a.js, 23, 3)) +>nest : Symbol(nest, Decl(a.js, 23, 27)) + +min.nest.other = self.min.nest.other || class { }; +>min.nest.other : Symbol((Anonymous function).other, Decl(a.js, 24, 44)) +>min.nest : Symbol(nest, Decl(a.js, 23, 27)) +>min : Symbol(min, Decl(a.js, 23, 3)) +>nest : Symbol(nest, Decl(a.js, 23, 27)) +>other : Symbol((Anonymous function).other, Decl(a.js, 24, 44)) + +min.property = global.min.property || {}; +>min.property : Symbol(property, Decl(a.js, 25, 50)) +>min : Symbol(min, Decl(a.js, 23, 3)) +>property : Symbol(property, Decl(a.js, 25, 50)) + diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.types b/tests/baselines/reference/typeFromPropertyAssignment9.types index 8da44fbe2cc..7fe591393bc 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.types +++ b/tests/baselines/reference/typeFromPropertyAssignment9.types @@ -1,66 +1,63 @@ === tests/cases/conformance/salsa/a.js === // TODO: JSDoc would provide a contextual type, so ... I should test that -// (a number of existing tests fail because of that, I think) -// TODO: Try initializer of function or class I guess (though classes aren't context sensitive) -// TODO: Duplicated declarations should be OK (if they have the same type (??)) var my = my || {}; ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->my || {} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->{} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>my || {} : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>{} : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } -my.m = function() { ->my.m = function() { return 1;} : () => number ->my.m : () => number ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->m : () => number +my.method = function() { +>my.method = function() { return 1;} : () => number +>my.method : () => number +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>method : () => number >function() { return 1;} : () => number return 1; >1 : 1 } -my.n = 1; ->my.n = 1 : 1 ->my.n : number ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->n : number +my.number = 1; +>my.number = 1 : 1 +>my.number : number +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>number : number >1 : 1 -my.o = {}; ->my.o = {} : { [x: string]: any; } ->my.o : { [x: string]: any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->o : { [x: string]: any; } +my.object = {}; +>my.object = {} : { [x: string]: any; } +>my.object : { [x: string]: any; } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>object : { [x: string]: any; } >{} : { [x: string]: any; } my.predicate = my.predicate || {}; ->my.predicate = my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->{} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate = my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>{} : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } my.predicate.query = function () { >my.predicate.query = function () { var me = this; me.property = false;} : { (): void; result: string; } >my.predicate.query : { (): void; result: string; } ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >query : { (): void; result: string; } >function () { var me = this; me.property = false;} : { (): void; result: string; } var me = this; ->me : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->this : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>me : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>this : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } me.property = false; >me.property = false : false >me.property : any ->me : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>me : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >property : any >false : false @@ -69,18 +66,18 @@ var q = new my.predicate.query(); >q : any >new my.predicate.query() : any >my.predicate.query : { (): void; result: string; } ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >query : { (): void; result: string; } my.predicate.query.result = 'none' >my.predicate.query.result = 'none' : "none" >my.predicate.query.result : string >my.predicate.query : { (): void; result: string; } ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >query : { (): void; result: string; } >result : string >'none' : "none" @@ -88,15 +85,15 @@ my.predicate.query.result = 'none' my.predicate.sort = my.predicate.sort || function (first, second) { >my.predicate.sort = my.predicate.sort || function (first, second) { return first;} : (first: any, second: any) => any >my.predicate.sort : (first: any, second: any) => any ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >sort : (first: any, second: any) => any >my.predicate.sort || function (first, second) { return first;} : (first: any, second: any) => any >my.predicate.sort : (first: any, second: any) => any ->my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; }; } ->predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } >sort : (first: any, second: any) => any >function (first, second) { return first;} : (first: any, second: any) => any >first : any @@ -105,4 +102,70 @@ my.predicate.sort = my.predicate.sort || function (first, second) { return first; >first : any } +my.predicate.type = class { +>my.predicate.type = class { m() { return 101; }} : typeof (Anonymous class) +>my.predicate.type : typeof (Anonymous class) +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>my : { [x: string]: any; method: () => number; number: number; object: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; sort: (first: any, second: any) => any; type: typeof (Anonymous class); } +>type : typeof (Anonymous class) +>class { m() { return 101; }} : typeof (Anonymous class) + + m() { return 101; } +>m : () => number +>101 : 101 +} + + +// global-ish prefixes +var min = window.min || {}; +>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; } +>window.min || {} : any +>window.min : any +>window : any +>min : any +>{} : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; } + +min.nest = this.min.nest || function () { }; +>min.nest = this.min.nest || function () { } : { (): void; other: typeof (Anonymous class); } +>min.nest : { (): void; other: typeof (Anonymous class); } +>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; } +>nest : { (): void; other: typeof (Anonymous class); } +>this.min.nest || function () { } : { (): void; other: typeof (Anonymous class); } +>this.min.nest : any +>this.min : any +>this : any +>min : any +>nest : any +>function () { } : { (): void; other: typeof (Anonymous class); } + +min.nest.other = self.min.nest.other || class { }; +>min.nest.other = self.min.nest.other || class { } : typeof (Anonymous class) +>min.nest.other : typeof (Anonymous class) +>min.nest : { (): void; other: typeof (Anonymous class); } +>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; } +>nest : { (): void; other: typeof (Anonymous class); } +>other : typeof (Anonymous class) +>self.min.nest.other || class { } : typeof (Anonymous class) +>self.min.nest.other : any +>self.min.nest : any +>self.min : any +>self : any +>min : any +>nest : any +>other : any +>class { } : typeof (Anonymous class) + +min.property = global.min.property || {}; +>min.property = global.min.property || {} : { [x: string]: any; } +>min.property : { [x: string]: any; } +>min : { [x: string]: any; nest: { (): void; other: typeof (Anonymous class); }; property: { [x: string]: any; }; } +>property : { [x: string]: any; } +>global.min.property || {} : { [x: string]: any; } +>global.min.property : any +>global.min : any +>global : any +>min : any +>property : any +>{} : { [x: string]: any; } diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment8.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment8.ts new file mode 100644 index 00000000000..0251e07c556 --- /dev/null +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment8.ts @@ -0,0 +1,31 @@ +// @noEmit: true +// @allowJs: true +// @checkJs: true +// @target: es6 +// @lib: es6,dom +// @Filename: a.js +var my = my || {}; +my.app = my.app || {}; + +my.app.Application = (function () { +var Application = function () { + //... +}; +return Application; +})(); +my.app.Application() + +// @Filename: b.js +var min = window.min || {}; +min.app = min.app || {}; + +min.app.Application = (function () { +var Application = function () { + //... +}; +return Application; +})(); +min.app.Application() + +// TODO: Make sure to include github.com/chadbr/vscodeTestJS +// (though extracted to 4 js files instead of 2 js files and an html) diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts index 9e0b796b2a3..1be3c506b9e 100644 --- a/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts @@ -5,15 +5,12 @@ // @Filename: a.js // TODO: JSDoc would provide a contextual type, so ... I should test that -// (a number of existing tests fail because of that, I think) -// TODO: Try initializer of function or class I guess (though classes aren't context sensitive) -// TODO: Duplicated declarations should be OK (if they have the same type (??)) var my = my || {}; -my.m = function() { +my.method = function() { return 1; } -my.n = 1; -my.o = {}; +my.number = 1; +my.object = {}; my.predicate = my.predicate || {}; my.predicate.query = function () { var me = this; @@ -24,3 +21,13 @@ my.predicate.query.result = 'none' my.predicate.sort = my.predicate.sort || function (first, second) { return first; } +my.predicate.type = class { + m() { return 101; } +} + + +// global-ish prefixes +var min = window.min || {}; +min.nest = this.min.nest || function () { }; +min.nest.other = self.min.nest.other || class { }; +min.property = global.min.property || {};