Skip to content

Commit b12947a

Browse files
authored
Merge pull request #18931 from amcasey/ExtractConstantThis
Allow Extract Constant into enclosing scope in spite of RangeFacts.UsesThis
2 parents 54ad9a6 + 73826bd commit b12947a

File tree

8 files changed

+133
-3
lines changed

8 files changed

+133
-3
lines changed

src/harness/unittests/extractConstants.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,30 @@ function f(): void { }
230230
testExtractConstantFailed("extractConstant_Never", `
231231
function f(): never { }
232232
[#|f();|]`);
233+
234+
testExtractConstant("extractConstant_This_Constructor", `
235+
class C {
236+
constructor() {
237+
[#|this.m2()|];
238+
}
239+
m2() { return 1; }
240+
}`);
241+
242+
testExtractConstant("extractConstant_This_Method", `
243+
class C {
244+
m1() {
245+
[#|this.m2()|];
246+
}
247+
m2() { return 1; }
248+
}`);
249+
250+
testExtractConstant("extractConstant_This_Property", `
251+
namespace N { // Force this test to be TS-only
252+
class C {
253+
x = 1;
254+
y = [#|this.x|];
255+
}
256+
}`);
233257
});
234258

235259
function testExtractConstant(caption: string, text: string) {

src/services/refactors/extractSymbol.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,10 @@ namespace ts.refactor.extractSymbol {
476476
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
477477
const containingClass = getContainingClass(current);
478478
if (containingClass) {
479-
return [containingClass];
479+
const containingFunction = findAncestor(current, isFunctionLikeDeclaration);
480+
return containingFunction
481+
? [containingFunction, containingClass]
482+
: [containingClass];
480483
}
481484
}
482485

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// ==ORIGINAL==
2+
3+
class C {
4+
constructor() {
5+
/*[#|*/this.m2()/*|]*/;
6+
}
7+
m2() { return 1; }
8+
}
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
11+
class C {
12+
constructor() {
13+
const /*RENAME*/newLocal = this.m2();
14+
}
15+
m2() { return 1; }
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ==ORIGINAL==
2+
3+
class C {
4+
constructor() {
5+
/*[#|*/this.m2()/*|]*/;
6+
}
7+
m2() { return 1; }
8+
}
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
11+
class C {
12+
constructor() {
13+
const /*RENAME*/newLocal = this.m2();
14+
}
15+
m2() { return 1; }
16+
}
17+
// ==SCOPE::Extract to readonly field in class 'C'==
18+
19+
class C {
20+
private readonly newProperty = this.m2();
21+
22+
constructor() {
23+
this./*RENAME*/newProperty;
24+
}
25+
m2() { return 1; }
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// ==ORIGINAL==
2+
3+
class C {
4+
m1() {
5+
/*[#|*/this.m2()/*|]*/;
6+
}
7+
m2() { return 1; }
8+
}
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
11+
class C {
12+
m1() {
13+
const /*RENAME*/newLocal = this.m2();
14+
}
15+
m2() { return 1; }
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ==ORIGINAL==
2+
3+
class C {
4+
m1() {
5+
/*[#|*/this.m2()/*|]*/;
6+
}
7+
m2() { return 1; }
8+
}
9+
// ==SCOPE::Extract to constant in enclosing scope==
10+
11+
class C {
12+
m1() {
13+
const /*RENAME*/newLocal = this.m2();
14+
}
15+
m2() { return 1; }
16+
}
17+
// ==SCOPE::Extract to readonly field in class 'C'==
18+
19+
class C {
20+
private readonly newProperty = this.m2();
21+
22+
m1() {
23+
this./*RENAME*/newProperty;
24+
}
25+
m2() { return 1; }
26+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// ==ORIGINAL==
2+
3+
namespace N { // Force this test to be TS-only
4+
class C {
5+
x = 1;
6+
y = /*[#|*/this.x/*|]*/;
7+
}
8+
}
9+
// ==SCOPE::Extract to readonly field in class 'C'==
10+
11+
namespace N { // Force this test to be TS-only
12+
class C {
13+
x = 1;
14+
private readonly newProperty = this.x;
15+
16+
y = this./*RENAME*/newProperty;
17+
}
18+
}

tests/cases/fourslash/extract-method20.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
//// }
1111

1212
goTo.select('a', 'b')
13-
verify.refactorAvailable('Extract Symbol', 'function_scope_0');
14-
verify.not.refactorAvailable('Extract Symbol', 'function_scope_1');
13+
verify.not.refactorAvailable('Extract Symbol', 'function_scope_0');
14+
verify.refactorAvailable('Extract Symbol', 'function_scope_1');
15+
verify.not.refactorAvailable('Extract Symbol', 'function_scope_2');

0 commit comments

Comments
 (0)