Skip to content

Commit 39b3ecb

Browse files
committed
Handel defining properties on function and class expressions in .js files
1 parent b8329a0 commit 39b3ecb

10 files changed

+94
-31
lines changed

src/compiler/binder.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,8 +2215,12 @@ namespace ts {
22152215
constructorFunction.parent = classPrototype;
22162216
classPrototype.parent = leftSideOfAssignment;
22172217

2218-
const funcSymbol = container.locals.get(constructorFunction.text);
2219-
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
2218+
let funcSymbol = container.locals.get(constructorFunction.text);
2219+
if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
2220+
funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol;
2221+
}
2222+
2223+
if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) {
22202224
return;
22212225
}
22222226

@@ -2241,8 +2245,12 @@ namespace ts {
22412245
leftSideOfAssignment.parent = node;
22422246
target.parent = leftSideOfAssignment;
22432247

2244-
const funcSymbol = container.locals[target.text];
2245-
if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
2248+
let funcSymbol = container.locals.get(target.text);
2249+
if (isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
2250+
funcSymbol = (funcSymbol.valueDeclaration as VariableDeclaration).initializer.symbol;
2251+
}
2252+
2253+
if (!funcSymbol || !(funcSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class))) {
22462254
return;
22472255
}
22482256

src/compiler/checker.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13886,10 +13886,13 @@ namespace ts {
1388613886
// in a JS file
1388713887
// Note:JS inferred classes might come from a variable declaration instead of a function declaration.
1388813888
// In this case, using getResolvedSymbol directly is required to avoid losing the members from the declaration.
13889-
const funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
13889+
let funcSymbol = node.expression.kind === SyntaxKind.Identifier ?
1389013890
getResolvedSymbol(node.expression as Identifier) :
1389113891
checkExpression(node.expression).symbol;
13892-
if (funcSymbol && funcSymbol.members && (funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) {
13892+
if (funcSymbol && isDeclarationOfFunctionOrClassExpression(funcSymbol)) {
13893+
funcSymbol = getSymbolOfNode((<VariableDeclaration>funcSymbol.valueDeclaration).initializer);
13894+
}
13895+
if (funcSymbol && funcSymbol.members && funcSymbol.flags & SymbolFlags.Function) {
1389313896
return getInferredClassType(funcSymbol);
1389413897
}
1389513898
else if (compilerOptions.noImplicitAny) {

src/compiler/utilities.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,18 +1366,14 @@ namespace ts {
13661366
* Returns true if the node is a variable declaration whose initializer is a function expression.
13671367
* This function does not test if the node is in a JavaScript file or not.
13681368
*/
1369-
export function isDeclarationOfFunctionExpression(s: Symbol) {
1369+
export function isDeclarationOfFunctionOrClassExpression(s: Symbol) {
13701370
if (s.valueDeclaration && s.valueDeclaration.kind === SyntaxKind.VariableDeclaration) {
13711371
const declaration = s.valueDeclaration as VariableDeclaration;
1372-
return declaration.initializer && declaration.initializer.kind === SyntaxKind.FunctionExpression;
1372+
return declaration.initializer && (declaration.initializer.kind === SyntaxKind.FunctionExpression || declaration.initializer.kind === SyntaxKind.ClassExpression);
13731373
}
13741374
return false;
13751375
}
13761376

1377-
export function isValidSpecialPropertyAssignmentParent(parentSymbol: Symbol) {
1378-
return parentSymbol && (parentSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(parentSymbol));
1379-
}
1380-
13811377
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
13821378
/// assignments we treat as special in the binder
13831379
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {

tests/baselines/reference/multipleDeclarations.symbols

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,41 +30,43 @@ class X {
3030
>this : Symbol(X, Decl(input.js, 5, 1))
3131

3232
this.mistake = 'frankly, complete nonsense';
33-
>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
33+
>this.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
3434
>this : Symbol(X, Decl(input.js, 5, 1))
3535
>mistake : Symbol(X.mistake, Decl(input.js, 8, 35))
3636
}
3737
m() {
3838
>m : Symbol(X.m, Decl(input.js, 10, 5))
3939
}
4040
mistake() {
41-
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
41+
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
4242
}
4343
}
4444
let x = new X();
4545
>x : Symbol(x, Decl(input.js, 16, 3))
4646
>X : Symbol(X, Decl(input.js, 5, 1))
4747

4848
X.prototype.mistake = false;
49-
>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
49+
>X.prototype.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
50+
>X.prototype : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
5051
>X : Symbol(X, Decl(input.js, 5, 1))
5152
>prototype : Symbol(X.prototype)
53+
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
5254

5355
x.m();
5456
>x.m : Symbol(X.m, Decl(input.js, 10, 5))
5557
>x : Symbol(x, Decl(input.js, 16, 3))
5658
>m : Symbol(X.m, Decl(input.js, 10, 5))
5759

5860
x.mistake;
59-
>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
61+
>x.mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
6062
>x : Symbol(x, Decl(input.js, 16, 3))
61-
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5))
63+
>mistake : Symbol(X.mistake, Decl(input.js, 12, 5), Decl(input.js, 16, 16))
6264

6365
class Y {
6466
>Y : Symbol(Y, Decl(input.js, 19, 10))
6567

6668
mistake() {
67-
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
69+
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
6870
}
6971
m() {
7072
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
@@ -80,15 +82,17 @@ class Y {
8082
>this : Symbol(Y, Decl(input.js, 19, 10))
8183

8284
this.mistake = 'even more nonsense';
83-
>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
85+
>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
8486
>this : Symbol(Y, Decl(input.js, 19, 10))
85-
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
87+
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
8688
}
8789
}
8890
Y.prototype.mistake = true;
89-
>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
91+
>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
92+
>Y.prototype : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
9093
>Y : Symbol(Y, Decl(input.js, 19, 10))
9194
>prototype : Symbol(Y.prototype)
95+
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
9296

9397
let y = new Y();
9498
>y : Symbol(y, Decl(input.js, 31, 3))
@@ -100,7 +104,7 @@ y.m();
100104
>m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19))
101105

102106
y.mistake();
103-
>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
107+
>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
104108
>y : Symbol(y, Decl(input.js, 31, 3))
105-
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35))
109+
>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1))
106110

tests/baselines/reference/multipleDeclarations.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@ class X {
4343

4444
this.mistake = 'frankly, complete nonsense';
4545
>this.mistake = 'frankly, complete nonsense' : "frankly, complete nonsense"
46-
>this.mistake : () => void
46+
>this.mistake : any
4747
>this : this
48-
>mistake : () => void
48+
>mistake : any
4949
>'frankly, complete nonsense' : "frankly, complete nonsense"
5050
}
5151
m() {
5252
>m : () => void
5353
}
5454
mistake() {
55-
>mistake : () => void
55+
>mistake : any
5656
}
5757
}
5858
let x = new X();
@@ -62,11 +62,11 @@ let x = new X();
6262

6363
X.prototype.mistake = false;
6464
>X.prototype.mistake = false : false
65-
>X.prototype.mistake : () => void
65+
>X.prototype.mistake : any
6666
>X.prototype : X
6767
>X : typeof X
6868
>prototype : X
69-
>mistake : () => void
69+
>mistake : any
7070
>false : false
7171

7272
x.m();
@@ -76,9 +76,9 @@ x.m();
7676
>m : () => void
7777

7878
x.mistake;
79-
>x.mistake : () => void
79+
>x.mistake : any
8080
>x : X
81-
>mistake : () => void
81+
>mistake : any
8282

8383
class Y {
8484
>Y : Y

tests/cases/fourslash/getJavaScriptCompletions20.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
//// Person.getNa/**/ = 10;
1919

2020
goTo.marker();
21-
verify.not.completionListContains('getNa');
21+
verify.completionListContains('getName');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @Filename: a.js
5+
////class Minimatch {
6+
////}
7+
////Minimatch.[|staticProperty|] = "string";
8+
////console.log(Minimatch./**/[|staticProperty|]);
9+
10+
goTo.marker();
11+
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @Filename: a.js
5+
////var C = class {
6+
////}
7+
////C.[|staticProperty|] = "string";
8+
////console.log(C./**/[|staticProperty|]);
9+
10+
goTo.marker();
11+
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @Filename: a.js
5+
////class C {
6+
//// constructor(y) {
7+
//// this.x = y;
8+
//// }
9+
////}
10+
////C.prototype.[|z|] = 1;
11+
////var t = new C(12);
12+
////t./**/[|z|] = 11;
13+
14+
goTo.marker();
15+
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @allowJs: true
4+
// @Filename: a.js
5+
////var C = class {
6+
//// constructor(y) {
7+
//// this.x = y;
8+
//// }
9+
////}
10+
////C.prototype.[|z|] = 1;
11+
////var t = new C(12);
12+
////t./**/[|z|] = 11;
13+
14+
goTo.marker();
15+
verify.renameLocations( /*findInStrings*/ false, /*findInComments*/ false);

0 commit comments

Comments
 (0)