diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts
index f8ddcaedbf0..e9d43a1a3c2 100644
--- a/src/services/refactors/extractType.ts
+++ b/src/services/refactors/extractType.ts
@@ -23,6 +23,7 @@ import {
getRefactorContextSpan,
getRenameLocation,
getTokenAtPosition,
+ getTouchingToken,
getUniqueName,
ignoreSourceNewlines,
isArray,
@@ -173,14 +174,9 @@ type ExtractInfo = TypeAliasInfo | InterfaceInfo;
function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined {
const { file, startPosition } = context;
const isJS = isSourceFileJS(file);
- const current = getTokenAtPosition(file, startPosition);
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
- const cursorRequest = range.pos === range.end && considerEmptySpans;
- const overlappingRange = nodeOverlapsWithStartEnd(current, file, range.pos, range.end);
-
- const firstType = findAncestor(current, node =>
- node.parent && isTypeNode(node) && !rangeContainsSkipTrivia(range, node.parent, file) &&
- (cursorRequest || overlappingRange));
+ const isCursorRequest = range.pos === range.end && considerEmptySpans;
+ const firstType = getFirstTypeAt(file, startPosition, range, isCursorRequest);
if (!firstType || !isTypeNode(firstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) };
const checker = context.program.getTypeChecker();
@@ -209,6 +205,24 @@ function getRangeToExtract(context: RefactorContext, considerEmptySpans = true):
return { isJS, selection, enclosingNode, typeParameters, typeElements };
}
+function getFirstTypeAt(file: SourceFile, startPosition: number, range: TextRange, isCursorRequest: boolean): Node | undefined {
+ const currentNodes = [
+ () => getTokenAtPosition(file, startPosition),
+ () => getTouchingToken(file, startPosition, () => true),
+ ];
+ for (const f of currentNodes) {
+ const current = f();
+ const overlappingRange = nodeOverlapsWithStartEnd(current, file, range.pos, range.end);
+ const firstType = findAncestor(current, node =>
+ node.parent && isTypeNode(node) && !rangeContainsSkipTrivia(range, node.parent, file) &&
+ (isCursorRequest || overlappingRange));
+ if (firstType) {
+ return firstType;
+ }
+ }
+ return undefined;
+}
+
function flattenTypeLiteralNodeReference(checker: TypeChecker, selection: TypeNode | TypeNode[] | undefined): readonly TypeElement[] | undefined {
if (!selection) return undefined;
if (isArray(selection)) {
diff --git a/tests/cases/fourslash/refactorExtractType89.ts b/tests/cases/fourslash/refactorExtractType89.ts
new file mode 100644
index 00000000000..bb6026cc7a6
--- /dev/null
+++ b/tests/cases/fourslash/refactorExtractType89.ts
@@ -0,0 +1,61 @@
+///
+
+//// interface Yadda { x: T }
+////
+//// export let blah: Yadda/*a*//*b*/;
+////
+//// interface YaddaWithDefault { x: T/*d*/ }
+
+goTo.marker("a");
+verify.refactorAvailableForTriggerReason("invoked", "Extract type", "Extract to type alias")
+
+goTo.marker("b");
+edit.applyRefactor({
+ triggerReason: "invoked",
+ refactorName: "Extract type",
+ actionName: "Extract to type alias",
+ actionDescription: "Extract to type alias",
+ newContent: `interface Yadda { x: T }
+
+type /*RENAME*/NewType = Yadda;
+
+export let blah: NewType;
+
+interface YaddaWithDefault { x: T }`,
+});
+
+goTo.marker("c");
+edit.applyRefactor({
+ triggerReason: "invoked",
+ refactorName: "Extract type",
+ actionName: "Extract to type alias",
+ actionDescription: "Extract to type alias",
+ newContent: `interface Yadda { x: T }
+
+type NewType = Yadda;
+
+export let blah: NewType;
+
+type /*RENAME*/NewType_1 = boolean;
+
+interface YaddaWithDefault { x: T }`
+});
+
+goTo.marker("d");
+edit.applyRefactor({
+ triggerReason: "invoked",
+ refactorName: "Extract type",
+ actionName: "Extract to type alias",
+ actionDescription: "Extract to type alias",
+ newContent: `interface Yadda { x: T }
+
+type NewType = Yadda;
+
+export let blah: NewType;
+
+type NewType_1 = boolean;
+
+type /*RENAME*/NewType_2 = T;
+
+interface YaddaWithDefault { x: NewType_2 }`
+});