diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index da1ba466530..50e69414907 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2215,8 +2215,12 @@ namespace ts { constructorFunction.parent = classPrototype; classPrototype.parent = leftSideOfAssignment; - const funcSymbol = container.locals.get(constructorFunction.text); - if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { + let funcSymbol = container.locals.get(constructorFunction.text); + if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) { + funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol; + } + + if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) { return; } @@ -2241,8 +2245,12 @@ namespace ts { leftSideOfAssignment.parent = node; target.parent = leftSideOfAssignment; - const funcSymbol = container.locals[target.text]; - if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { + let funcSymbol = container.locals.get(target.text); + if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) { + funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol; + } + + if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) { return; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 78f67ae438f..b7c81c1e0ab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13886,10 +13886,13 @@ namespace ts { // in a JS file // Note:JS inferred classes might come from a variable declaration instead of a function declaration. // In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration. - const funcSymbol = node.expression.kind === SyntaxKind.Identifier ? + let funcSymbol = node.expression.kind === SyntaxKind.Identifier ? getResolvedSymbol(node.expression as Identifier) : checkExpression(node.expression).symbol; - if (funcSymbol && funcSymbol.members && (funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { + if (funcSymbol && isDeclarationOfFunctionOrClassExpression(funcSymbol)) { + funcSymbol = getSymbolOfNode((funcSymbol.valueDeclaration).initializer); + } + if (funcSymbol && funcSymbol.members && funcSymbol.flags & SymbolFlags.Function) { return getInferredClassType(funcSymbol); } else if (compilerOptions.noImplicitAny) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5cd0746b039..40bfc2302dc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1366,18 +1366,14 @@ namespace ts { * Returns true if the node is a variable declaration whose initializer is a function expression. * This function does not test if the node is in a JavaScript file or not. */ - export function isDeclarationOfFunctionExpression(s: Symbol) { + export function isDeclarationOfFunctionOrClassExpression(s: Symbol) { if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) { const declaration = s.valueDeclaration as VariableDeclaration; - return declaration.initializer && declaration.initializer.kind === SyntaxKind.FunctionExpression; + return declaration.initializer && (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression); } return false; } - export function isValidSpecialPropertyAssignmentParent(parentSymbol: Symbol) { - return parentSymbol && (parentSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(parentSymbol)); - } - /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind { diff --git a/tests/baselines/reference/multipleDeclarations.symbols b/tests/baselines/reference/multipleDeclarations.symbols index 9e49c2fc00c..4fc8f8a5f28 100644 --- a/tests/baselines/reference/multipleDeclarations.symbols +++ b/tests/baselines/reference/multipleDeclarations.symbols @@ -30,7 +30,7 @@ class X { >this : Symbol(X, Decl(input.js, 5, 1)) this.mistake = 'frankly, complete nonsense'; ->this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) >this : Symbol(X, Decl(input.js, 5, 1)) >mistake : Symbol(X.mistake, Decl(input.js, 8, 35)) } @@ -38,7 +38,7 @@ class X { >m : Symbol(X.m, Decl(input.js, 10, 5)) } mistake() { ->mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) } } let x = new X(); @@ -46,9 +46,11 @@ let x = new X(); >X : Symbol(X, Decl(input.js, 5, 1)) X.prototype.mistake = false; ->X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) +>X.prototype : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) >X : Symbol(X, Decl(input.js, 5, 1)) >prototype : Symbol(X.prototype) +>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) x.m(); >x.m : Symbol(X.m, Decl(input.js, 10, 5)) @@ -56,15 +58,15 @@ x.m(); >m : Symbol(X.m, Decl(input.js, 10, 5)) x.mistake; ->x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) >x : Symbol(x, Decl(input.js, 16, 3)) ->mistake : Symbol(X.mistake, Decl(input.js, 12, 5)) +>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16)) class Y { >Y : Symbol(Y, Decl(input.js, 19, 10)) mistake() { ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) } m() { >m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) @@ -80,15 +82,17 @@ class Y { >this : Symbol(Y, Decl(input.js, 19, 10)) this.mistake = 'even more nonsense'; ->this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) >this : Symbol(Y, Decl(input.js, 19, 10)) ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) } } Y.prototype.mistake = true; ->Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>Y.prototype : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) >Y : Symbol(Y, Decl(input.js, 19, 10)) >prototype : Symbol(Y.prototype) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) let y = new Y(); >y : Symbol(y, Decl(input.js, 31, 3)) @@ -100,7 +104,7 @@ y.m(); >m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) y.mistake(); ->y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) >y : Symbol(y, Decl(input.js, 31, 3)) ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index 195bdc2ad0d..b82cdbe05ed 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -43,16 +43,16 @@ class X { this.mistake = 'frankly, complete nonsense'; >this.mistake = 'frankly, complete nonsense' : "frankly, complete nonsense" ->this.mistake : () => void +>this.mistake : any >this : this ->mistake : () => void +>mistake : any >'frankly, complete nonsense' : "frankly, complete nonsense" } m() { >m : () => void } mistake() { ->mistake : () => void +>mistake : any } } let x = new X(); @@ -62,11 +62,11 @@ let x = new X(); X.prototype.mistake = false; >X.prototype.mistake = false : false ->X.prototype.mistake : () => void +>X.prototype.mistake : any >X.prototype : X >X : typeof X >prototype : X ->mistake : () => void +>mistake : any >false : false x.m(); @@ -76,9 +76,9 @@ x.m(); >m : () => void x.mistake; ->x.mistake : () => void +>x.mistake : any >x : X ->mistake : () => void +>mistake : any class Y { >Y : Y diff --git a/tests/cases/fourslash/getJavaScriptCompletions20.ts b/tests/cases/fourslash/getJavaScriptCompletions20.ts index 3ca27fa288a..ec2bcef161b 100644 --- a/tests/cases/fourslash/getJavaScriptCompletions20.ts +++ b/tests/cases/fourslash/getJavaScriptCompletions20.ts @@ -18,4 +18,4 @@ //// Person.getNa/**/ = 10; goTo.marker(); -verify.not.completionListContains('getNa'); +verify.completionListContains('getName'); diff --git a/tests/cases/fourslash/renameJsPropertyAssignment2.ts b/tests/cases/fourslash/renameJsPropertyAssignment2.ts new file mode 100644 index 00000000000..39831d4ae1b --- /dev/null +++ b/tests/cases/fourslash/renameJsPropertyAssignment2.ts @@ -0,0 +1,11 @@ +/// + +// @allowJs: true +// @Filename: a.js +////class Minimatch { +////} +////Minimatch.[|staticProperty|] = "string"; +////console.log(Minimatch./**/[|staticProperty|]); + +goTo.marker(); +verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false); \ No newline at end of file diff --git a/tests/cases/fourslash/renameJsPropertyAssignment3.ts b/tests/cases/fourslash/renameJsPropertyAssignment3.ts new file mode 100644 index 00000000000..5871010d6f4 --- /dev/null +++ b/tests/cases/fourslash/renameJsPropertyAssignment3.ts @@ -0,0 +1,11 @@ +/// + +// @allowJs: true +// @Filename: a.js +////var C = class { +////} +////C.[|staticProperty|] = "string"; +////console.log(C./**/[|staticProperty|]); + +goTo.marker(); +verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false); \ No newline at end of file diff --git a/tests/cases/fourslash/renameJsThisProperty05.ts b/tests/cases/fourslash/renameJsThisProperty05.ts new file mode 100644 index 00000000000..c1408d6aff1 --- /dev/null +++ b/tests/cases/fourslash/renameJsThisProperty05.ts @@ -0,0 +1,15 @@ +/// + +// @allowJs: true +// @Filename: a.js +////class C { +//// constructor(y) { +//// this.x = y; +//// } +////} +////C.prototype.[|z|] = 1; +////var t = new C(12); +////t./**/[|z|] = 11; + +goTo.marker(); +verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false); diff --git a/tests/cases/fourslash/renameJsThisProperty06.ts b/tests/cases/fourslash/renameJsThisProperty06.ts new file mode 100644 index 00000000000..63a16b68a4f --- /dev/null +++ b/tests/cases/fourslash/renameJsThisProperty06.ts @@ -0,0 +1,15 @@ +/// + +// @allowJs: true +// @Filename: a.js +////var C = class { +//// constructor(y) { +//// this.x = y; +//// } +////} +////C.prototype.[|z|] = 1; +////var t = new C(12); +////t./**/[|z|] = 11; + +goTo.marker(); +verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);