From 2d501b1d98e0424991e95cf0038afd97520b65bc Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 28 Jan 2022 22:19:48 +0200 Subject: [PATCH] fix(47582): skip extraction if the type node is in the range of the type parameter declaration (#47596) --- src/services/refactors/extractType.ts | 19 ++++++++++++++----- .../cases/fourslash/refactorExtractType72.ts | 6 ++++++ .../cases/fourslash/refactorExtractType73.ts | 11 +++++++++++ .../cases/fourslash/refactorExtractType74.ts | 15 +++++++++++++++ .../cases/fourslash/refactorExtractType75.ts | 9 +++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/cases/fourslash/refactorExtractType72.ts create mode 100644 tests/cases/fourslash/refactorExtractType73.ts create mode 100644 tests/cases/fourslash/refactorExtractType74.ts create mode 100644 tests/cases/fourslash/refactorExtractType75.ts diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index e932c9fcc15..0e16def63fe 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -144,11 +144,20 @@ namespace ts.refactor { function visitor(node: Node): true | undefined { if (isTypeReferenceNode(node)) { if (isIdentifier(node.typeName)) { - const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true); - const declaration = tryCast(symbol?.declarations?.[0], isTypeParameterDeclaration); - if (declaration) { - if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) { - pushIfUnique(result, declaration); + const typeName = node.typeName; + const symbol = checker.resolveName(typeName.text, typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true); + for (const decl of symbol?.declarations || emptyArray) { + if (isTypeParameterDeclaration(decl) && decl.getSourceFile() === file) { + // skip extraction if the type node is in the range of the type parameter declaration. + // function foo(): void; + if (decl.name.escapedText === typeName.escapedText && rangeContainsSkipTrivia(decl, selection, file)) { + return true; + } + + if (rangeContainsSkipTrivia(statement, decl, file) && !rangeContainsSkipTrivia(selection, decl, file)) { + pushIfUnique(result, decl); + break; + } } } } diff --git a/tests/cases/fourslash/refactorExtractType72.ts b/tests/cases/fourslash/refactorExtractType72.ts new file mode 100644 index 00000000000..d56f71938e8 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType72.ts @@ -0,0 +1,6 @@ +/// + +////export declare function foo(): void; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type", "Extract to type alias"); diff --git a/tests/cases/fourslash/refactorExtractType73.ts b/tests/cases/fourslash/refactorExtractType73.ts new file mode 100644 index 00000000000..8eab5d632a7 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType73.ts @@ -0,0 +1,11 @@ +/// + +// @Filename: a.ts +////interface Foo {} + +// @Filename: b.ts +////interface Foo {} + +goTo.file("b.ts"); +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type", "Extract to type alias"); diff --git a/tests/cases/fourslash/refactorExtractType74.ts b/tests/cases/fourslash/refactorExtractType74.ts new file mode 100644 index 00000000000..5434c8c9b27 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType74.ts @@ -0,0 +1,15 @@ +/// + +// @Filename: a.ts +//// interface Foo {} + +// @Filename: b.ts +//// // Some initial comments. +//// // We need to ensure these files have different contents, +//// // so their ranges differ, so we'll start a few lines below in this file. +//// interface Foo {} + +for (const range of test.ranges()) { + goTo.selectRange(range); + verify.not.refactorAvailable("Extract type", "Extract to type alias"); +} diff --git a/tests/cases/fourslash/refactorExtractType75.ts b/tests/cases/fourslash/refactorExtractType75.ts new file mode 100644 index 00000000000..f213cdd80da --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType75.ts @@ -0,0 +1,9 @@ +/// + +// @Filename: a.ts +//// interface Foo {} +//// interface Foo {} + +goTo.file("a.ts"); +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type", "Extract to type alias");