Skip to content

Commit 80c04f8

Browse files
authored
Merge pull request #10358 from Microsoft/classPropertyInference
Support this.prop = expr; assignments as declarations for ES6 JS classes — Take 2
2 parents 11f27e8 + f3c085e commit 80c04f8

File tree

6 files changed

+123
-1
lines changed

6 files changed

+123
-1
lines changed

src/compiler/binder.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1972,7 +1972,18 @@ namespace ts {
19721972
assignee = container;
19731973
}
19741974
else if (container.kind === SyntaxKind.Constructor) {
1975-
assignee = container.parent;
1975+
if (isInJavaScriptFile(node)) {
1976+
// this.foo assignment in a JavaScript class
1977+
// Bind this property to the containing class
1978+
const saveContainer = container;
1979+
container = container.parent;
1980+
bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None);
1981+
container = saveContainer;
1982+
return;
1983+
}
1984+
else {
1985+
assignee = container.parent;
1986+
}
19761987
}
19771988
else {
19781989
return;

src/compiler/checker.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3248,6 +3248,13 @@ namespace ts {
32483248
// * className.prototype.method = expr
32493249
if (declaration.kind === SyntaxKind.BinaryExpression ||
32503250
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
3251+
// Use JS Doc type if present on parent expression statement
3252+
if (declaration.flags & NodeFlags.JavaScriptFile) {
3253+
const typeTag = getJSDocTypeTag(declaration.parent);
3254+
if (typeTag && typeTag.typeExpression) {
3255+
return links.type = getTypeFromTypeNode(typeTag.typeExpression.type);
3256+
}
3257+
}
32513258
const declaredTypes = map(symbol.declarations,
32523259
decl => decl.kind === SyntaxKind.BinaryExpression ?
32533260
checkExpressionCached((<BinaryExpression>decl).right) :
@@ -9456,6 +9463,11 @@ namespace ts {
94569463
const binaryExpression = <BinaryExpression>node.parent;
94579464
const operator = binaryExpression.operatorToken.kind;
94589465
if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) {
9466+
// Don't do this for special property assignments to avoid circularity
9467+
if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) {
9468+
return undefined;
9469+
}
9470+
94599471
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
94609472
if (node === binaryExpression.right) {
94619473
return checkExpression(binaryExpression.left);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// Classes have their shape inferred from assignments
4+
// to properties of 'this' in the constructor
5+
6+
// @allowNonTsExtensions: true
7+
// @Filename: Foo.js
8+
//// class Foo {
9+
//// constructor() {
10+
//// this.bar = 'world';
11+
//// this.thing = () => 0;
12+
//// this.union = 'foo';
13+
//// this.union = 100;
14+
//// }
15+
//// }
16+
//// var x = new Foo();
17+
//// x/**/
18+
19+
20+
goTo.marker();
21+
edit.insert('.');
22+
verify.completionListContains("bar", /*displayText*/ undefined, /*documentation*/ undefined, "property");
23+
verify.completionListContains("thing", /*displayText*/ undefined, /*documentation*/ undefined, "property");
24+
verify.completionListContains("union", /*displayText*/ undefined, /*documentation*/ undefined, "property");
25+
26+
edit.insert('bar.');
27+
verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method");
28+
edit.backspace('bar.'.length);
29+
30+
edit.insert('union.');
31+
verify.completionListContains("toString", /*displayText*/ undefined, /*documentation*/ undefined, "method");
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// In an inferred class, we can rename successfully
4+
5+
// @allowNonTsExtensions: true
6+
// @Filename: Foo.js
7+
//// class Foo {
8+
//// constructor() {
9+
//// this.[|union|] = 'foo';
10+
//// this./*1*/[|union|] = 100;
11+
//// }
12+
//// method() { return this./*2*/[|union|]; }
13+
//// }
14+
//// var x = new Foo();
15+
//// x./*3*/[|union|];
16+
17+
goTo.marker('1');
18+
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
19+
goTo.marker('2');
20+
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
21+
goTo.marker('3');
22+
verify.renameLocations(/*findInStrings*/false, /*findInComments*/false);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// In an inferred class, we can to-to-def successfully
4+
5+
// @allowNonTsExtensions: true
6+
// @Filename: Foo.js
7+
//// class Foo {
8+
//// constructor() {
9+
//// /*dst1*/this.alpha = 10;
10+
//// /*dst2*/this.beta = 'gamma';
11+
//// }
12+
//// method() { return this.alpha; }
13+
//// }
14+
//// var x = new Foo();
15+
//// x.alpha/*src1*/;
16+
//// x.beta/*src2*/;
17+
18+
goTo.marker('src1');
19+
goTo.definition();
20+
verify.caretAtMarker('dst1');
21+
22+
goTo.marker('src2');
23+
goTo.definition();
24+
verify.caretAtMarker('dst2');
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// Classes have their shape inferred from assignments
4+
// to properties of 'this' in the constructor
5+
6+
// @allowNonTsExtensions: true
7+
// @Filename: Foo.js
8+
//// class Foo {
9+
//// constructor() {
10+
//// /**
11+
//// * @type {string}
12+
//// */
13+
//// this.baz = null;
14+
//// }
15+
//// }
16+
//// var x = new Foo();
17+
//// x/**/
18+
19+
goTo.marker();
20+
edit.insert('.baz.');
21+
verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method");
22+

0 commit comments

Comments
 (0)