diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts
index 4fadc5fdefb..9efcce30066 100644
--- a/src/services/refactors/extractSymbol.ts
+++ b/src/services/refactors/extractSymbol.ts
@@ -111,6 +111,7 @@ import {
LabeledStatement,
last,
map,
+ mapDefined,
MethodDeclaration,
Modifier,
ModifierFlags,
@@ -1070,12 +1071,12 @@ function extractFunctionInScope(
callArguments.push(factory.createIdentifier(name));
});
- const typeParametersAndDeclarations = arrayFrom(typeParameterUsages.values(), type => ({ type, declaration: getFirstDeclaration(type) }));
+ const typeParametersAndDeclarations = arrayFrom(typeParameterUsages.values(), type => ({ type, declaration: getFirstDeclarationBeforePosition(type, context.startPosition) }));
const sortedTypeParametersAndDeclarations = typeParametersAndDeclarations.sort(compareTypesByDeclarationOrder);
const typeParameters: readonly TypeParameterDeclaration[] | undefined = sortedTypeParametersAndDeclarations.length === 0
? undefined
- : sortedTypeParametersAndDeclarations.map(t => t.declaration as TypeParameterDeclaration);
+ : mapDefined(sortedTypeParametersAndDeclarations, ({ declaration }) => declaration as TypeParameterDeclaration);
// Strictly speaking, we should check whether each name actually binds to the appropriate type
// parameter. In cases of shadowing, they may not.
@@ -1547,13 +1548,13 @@ function getContainingVariableDeclarationIfInList(node: Node, scope: Scope) {
}
}
-function getFirstDeclaration(type: Type): Declaration | undefined {
+function getFirstDeclarationBeforePosition(type: Type, position: number): Declaration | undefined {
let firstDeclaration;
const symbol = type.symbol;
if (symbol && symbol.declarations) {
for (const declaration of symbol.declarations) {
- if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) {
+ if ((firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) && declaration.pos < position) {
firstDeclaration = declaration;
}
}
diff --git a/tests/cases/fourslash/extract-method-generics-inside-extraction1.ts b/tests/cases/fourslash/extract-method-generics-inside-extraction1.ts
new file mode 100644
index 00000000000..f585b96e918
--- /dev/null
+++ b/tests/cases/fourslash/extract-method-generics-inside-extraction1.ts
@@ -0,0 +1,22 @@
+///
+
+////function f() {
+//// let g = /*start*/(x: T) => x/*end*/;
+//// return g;
+////}
+
+goTo.select('start', 'end');
+edit.applyRefactor({
+ refactorName: "Extract Symbol",
+ actionName: "function_scope_1",
+ actionDescription: "Extract to function in global scope",
+ newContent:
+`function f() {
+ let g = /*RENAME*/newFunction();
+ return g;
+}
+
+function newFunction() {
+ return (x: T) => x;
+}
+`});
diff --git a/tests/cases/fourslash/extract-method-generics-inside-extraction2.ts b/tests/cases/fourslash/extract-method-generics-inside-extraction2.ts
new file mode 100644
index 00000000000..62982480124
--- /dev/null
+++ b/tests/cases/fourslash/extract-method-generics-inside-extraction2.ts
@@ -0,0 +1,20 @@
+///
+
+////function satisfies() {
+//// return /*start*/(x: T) => x/*end*/;
+////}
+
+goTo.select('start', 'end');
+edit.applyRefactor({
+ refactorName: "Extract Symbol",
+ actionName: "function_scope_1",
+ actionDescription: "Extract to function in global scope",
+ newContent:
+`function satisfies() {
+ return /*RENAME*/newFunction();
+}
+
+function newFunction() {
+ return (x: T) => x;
+}
+`});