Skip to content

Commit ac286d6

Browse files
committed
fix(47582): skip extraction if the type node is in the range of the type parameter declaration
1 parent a9efe2b commit ac286d6

File tree

5 files changed

+55
-5
lines changed

5 files changed

+55
-5
lines changed

src/services/refactors/extractType.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,20 @@ namespace ts.refactor {
144144
function visitor(node: Node): true | undefined {
145145
if (isTypeReferenceNode(node)) {
146146
if (isIdentifier(node.typeName)) {
147-
const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true);
148-
const declaration = tryCast(symbol?.declarations?.[0], isTypeParameterDeclaration);
149-
if (declaration) {
150-
if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) {
151-
pushIfUnique(result, declaration);
147+
const typeName = node.typeName;
148+
const symbol = checker.resolveName(typeName.text, typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true);
149+
for (const decl of symbol?.declarations || emptyArray) {
150+
if (isTypeParameterDeclaration(decl) && decl.getSourceFile() === file) {
151+
// skip extraction if the type node is in the range of the type parameter declaration.
152+
// function foo<T extends { a?: /**/T }>(): void;
153+
if (decl.name.escapedText === typeName.escapedText && rangeContainsSkipTrivia(decl, selection, file)) {
154+
return true;
155+
}
156+
157+
if (rangeContainsSkipTrivia(statement, decl, file) && !rangeContainsSkipTrivia(selection, decl, file)) {
158+
pushIfUnique(result, decl);
159+
break;
160+
}
152161
}
153162
}
154163
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////export declare function foo<T extends { a?: /*a*/T/*b*/ }>(): void;
4+
5+
goTo.select("a", "b");
6+
verify.not.refactorAvailable("Extract type", "Extract to type alias");
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+
// @Filename: a.ts
4+
////interface Foo<T extends { prop: T }> {}
5+
6+
// @Filename: b.ts
7+
////interface Foo<T extends { prop: /*a*/T/*b*/ }> {}
8+
9+
goTo.file("b.ts");
10+
goTo.select("a", "b");
11+
verify.not.refactorAvailable("Extract type", "Extract to type alias");
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+
// @Filename: a.ts
4+
//// interface Foo<T extends { prop: [|T|] }> {}
5+
6+
// @Filename: b.ts
7+
//// // Some initial comments.
8+
//// // We need to ensure these files have different contents,
9+
//// // so their ranges differ, so we'll start a few lines below in this file.
10+
//// interface Foo<T extends { prop: [|T|] }> {}
11+
12+
for (const range of test.ranges()) {
13+
goTo.selectRange(range);
14+
verify.not.refactorAvailable("Extract type", "Extract to type alias");
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: a.ts
4+
//// interface Foo<T extends { prop: T }> {}
5+
//// interface Foo<T extends { prop: /*a*/T/*b*/ }> {}
6+
7+
goTo.file("a.ts");
8+
goTo.select("a", "b");
9+
verify.not.refactorAvailable("Extract type", "Extract to type alias");

0 commit comments

Comments
 (0)