From a09c2391a4d8057d13f25086fdd3b76879b690e9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 8 Feb 2018 16:07:22 -0800 Subject: [PATCH] 4-nested object-literal assignment works in JS --- src/compiler/binder.ts | 5 +- src/compiler/utilities.ts | 11 +++ .../typeFromPropertyAssignment9.symbols | 14 +++- .../typeFromPropertyAssignment9.types | 78 +++++++++++-------- .../salsa/typeFromPropertyAssignment9.ts | 8 +- 5 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0e91e752376..4dd3dc90e67 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2406,8 +2406,8 @@ namespace ts { let symbol = lookupSymbolForPropertyAccess(node.expression as Identifier | PropertyAccessExpression); symbol = symbol && isDeclarationOfJavascriptContainerExpression(symbol) ? (symbol.valueDeclaration as VariableDeclaration).initializer.symbol : symbol && isDeclarationOfDefaultedJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration as VariableDeclaration).initializer as BinaryExpression).right.symbol : - // TODO: Might want the next line below for even further nestings? - // symbol && isAssignmentOfDefaultedJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration.parent as BinaryExpression).right as BinaryExpression).symbol : + symbol && isAssignmentOfDefaultedJavascriptContainerExpression(symbol) ? (((symbol.valueDeclaration.parent as BinaryExpression).right as BinaryExpression).right as BinaryExpression).symbol : + symbol && isAssignmentOfJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration.parent as BinaryExpression).right as BinaryExpression).symbol : symbol; return symbol && symbol.exports && symbol.exports.get(node.name.escapedText); } @@ -2420,6 +2420,7 @@ namespace ts { let targetSymbol = symbol && isDeclarationOfJavascriptContainerExpression(symbol) ? (symbol.valueDeclaration as VariableDeclaration).initializer.symbol : symbol && isDeclarationOfDefaultedJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration as VariableDeclaration).initializer as BinaryExpression).right.symbol : symbol && isAssignmentOfDefaultedJavascriptContainerExpression(symbol) ? (((symbol.valueDeclaration.parent as BinaryExpression).right as BinaryExpression).right as BinaryExpression).symbol : + symbol && isAssignmentOfJavascriptContainerExpression(symbol) ? ((symbol.valueDeclaration.parent as BinaryExpression).right as BinaryExpression).symbol : symbol; Debug.assert(propertyAccess.parent.kind === SyntaxKind.BinaryExpression || propertyAccess.parent.kind === SyntaxKind.ExpressionStatement || diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 966909f5284..f779fd19e11 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1472,6 +1472,7 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote; } + // TODO: All 5 (!) of these need to be de-duped /** * Returns true if the node is a variable declaration whose initializer is a function or class expression. * This function does not test if the node is in a JavaScript file or not. @@ -1511,6 +1512,16 @@ namespace ts { s.valueDeclaration.initializer.right.properties.length === 0; } + export function isAssignmentOfJavascriptContainerExpression(s: Symbol) { + return s.valueDeclaration && + isPropertyAccessExpression(s.valueDeclaration) && + isBinaryExpression(s.valueDeclaration.parent) && + s.valueDeclaration.parent.right && + (s.valueDeclaration.parent.right.kind === SyntaxKind.FunctionExpression || + s.valueDeclaration.parent.right.kind === SyntaxKind.ClassExpression || + isObjectLiteralExpression(s.valueDeclaration.parent.right) && s.valueDeclaration.parent.right.properties.length === 0); + } + export function isAssignmentOfDefaultedJavascriptContainerExpression(s: Symbol) { return s.valueDeclaration && isPropertyAccessExpression(s.valueDeclaration) && diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.symbols b/tests/baselines/reference/typeFromPropertyAssignment9.symbols index c1afbe82036..2afbefe6d39 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.symbols +++ b/tests/baselines/reference/typeFromPropertyAssignment9.symbols @@ -55,8 +55,16 @@ var q = new my.predicate.query(); >predicate : Symbol(predicate, Decl(a.js, 9, 10), Decl(a.js, 10, 34)) >query : Symbol(query, Decl(a.js, 10, 34)) -// my.predicate.result = {}; -// my.predicate.sorted = my.predicate.sorted || {}; +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)) -// TODO: EVEN MORE NESTING +// my.predicate.sort = my.predicate.sort || function (first, second) { + // return first; +// } diff --git a/tests/baselines/reference/typeFromPropertyAssignment9.types b/tests/baselines/reference/typeFromPropertyAssignment9.types index 88607d08cf1..9c6981b63c8 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment9.types +++ b/tests/baselines/reference/typeFromPropertyAssignment9.types @@ -4,15 +4,15 @@ // 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; }; } ->my || {} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->{} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>my || {} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>{} : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } 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; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } >m : () => number >function() { return 1;} : () => number @@ -22,45 +22,45 @@ my.m = function() { 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; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } >n : 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; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } >o : { [x: string]: any; } >{} : { [x: string]: any; } my.predicate = my.predicate || {}; ->my.predicate = my.predicate || {} : { [x: string]: any; query: () => void; } ->my.predicate : { [x: string]: any; query: () => void; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->predicate : { [x: string]: any; query: () => void; } ->my.predicate || {} : { [x: string]: any; query: () => void; } ->my.predicate : { [x: string]: any; query: () => void; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->predicate : { [x: string]: any; query: () => void; } ->{} : { [x: string]: any; query: () => void; } +>my.predicate = my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; } +>my.predicate || {} : { [x: string]: any; query: { (): void; result: string; }; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; } +>{} : { [x: string]: any; query: { (): void; result: string; }; } my.predicate.query = function () { ->my.predicate.query = function () { var me = this; me.property = false;} : () => void ->my.predicate.query : () => void ->my.predicate : { [x: string]: any; query: () => void; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->predicate : { [x: string]: any; query: () => void; } ->query : () => void ->function () { var me = this; me.property = false;} : () => void +>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; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; } +>query : { (): void; result: string; } +>function () { var me = this; me.property = false;} : { (): void; result: string; } var me = this; ->me : { [x: string]: any; query: () => void; } ->this : { [x: string]: any; query: () => void; } +>me : { [x: string]: any; query: { (): void; result: string; }; } +>this : { [x: string]: any; query: { (): void; result: string; }; } me.property = false; >me.property = false : false >me.property : any ->me : { [x: string]: any; query: () => void; } +>me : { [x: string]: any; query: { (): void; result: string; }; } >property : any >false : false @@ -68,14 +68,24 @@ my.predicate.query = function () { var q = new my.predicate.query(); >q : any >new my.predicate.query() : any ->my.predicate.query : () => void ->my.predicate : { [x: string]: any; query: () => void; } ->my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: () => void; }; } ->predicate : { [x: string]: any; query: () => void; } ->query : () => void +>my.predicate.query : { (): void; result: string; } +>my.predicate : { [x: string]: any; query: { (): void; result: string; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; } +>query : { (): void; result: string; } -// my.predicate.result = {}; -// my.predicate.sorted = my.predicate.sorted || {}; +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; }; } +>my : { [x: string]: any; m: () => number; n: number; o: { [x: string]: any; }; predicate: { [x: string]: any; query: { (): void; result: string; }; }; } +>predicate : { [x: string]: any; query: { (): void; result: string; }; } +>query : { (): void; result: string; } +>result : string +>'none' : "none" -// TODO: EVEN MORE NESTING +// my.predicate.sort = my.predicate.sort || function (first, second) { + // return first; +// } diff --git a/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts b/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts index d3a339b8804..d6da4385625 100644 --- a/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts +++ b/tests/cases/conformance/salsa/typeFromPropertyAssignment9.ts @@ -20,7 +20,7 @@ my.predicate.query = function () { me.property = false; }; var q = new my.predicate.query(); -// my.predicate.result = {}; -// my.predicate.sorted = my.predicate.sorted || {}; - -// TODO: EVEN MORE NESTING +my.predicate.query.result = 'none' +// my.predicate.sort = my.predicate.sort || function (first, second) { + // return first; +// }