diff --git a/src/services/refactors/helpers.ts b/src/services/refactors/helpers.ts index 459979c3ef0dc..8b7fb3c729973 100644 --- a/src/services/refactors/helpers.ts +++ b/src/services/refactors/helpers.ts @@ -10,6 +10,7 @@ import { isClassLike, isPrivateIdentifier, isPropertyAccessExpression, + isSourceFile, ModuleBlock, Node, Program, @@ -76,7 +77,7 @@ export function addTargetFileImports( * So in that case, fall back to copying the import verbatim. */ importsToCopy.forEach(([isValidTypeOnlyUseSite, declaration], symbol) => { - const targetSymbol = skipAlias(symbol, checker); + const targetSymbol = resolveTargetSymbol(checker, symbol); if (checker.isUnknownSymbol(targetSymbol)) { importAdder.addVerbatimImport(Debug.checkDefined(declaration ?? findAncestor(symbol.declarations?.[0], isAnyImportOrRequireStatement))); } @@ -87,3 +88,11 @@ export function addTargetFileImports( addImportsForMovedSymbols(targetFileImportsFromOldFile, oldFile.fileName, importAdder, program); } + +function resolveTargetSymbol(checker: TypeChecker, symbol: Symbol) { + if (symbol.flags & SymbolFlags.Alias) { + const targetSymbol = skipAlias(symbol, checker); + return targetSymbol.declarations?.some(isSourceFile) && targetSymbol.exports?.has(symbol.escapedName) ? targetSymbol.exports.get(symbol.escapedName) as Symbol : targetSymbol; + } + return symbol; +} diff --git a/src/services/refactors/moveToFile.ts b/src/services/refactors/moveToFile.ts index da6d806a06f30..90fea44773e49 100644 --- a/src/services/refactors/moveToFile.ts +++ b/src/services/refactors/moveToFile.ts @@ -71,6 +71,7 @@ import { hasTSFileExtension, hostGetCanonicalFileName, Identifier, + idText, ImportDeclaration, ImportEqualsDeclaration, importFromModuleSpecifier, @@ -105,6 +106,7 @@ import { isStatement, isStringLiteral, isStringLiteralLike, + isTransientSymbol, isValidTypeOnlyAliasUseSite, isVariableDeclaration, isVariableDeclarationInitializedToRequire, @@ -967,7 +969,7 @@ function forEachReference(node: Node, checker: TypeChecker, enclosingRange: Text if (enclosingRange && !rangeContainsRange(enclosingRange, node)) { return; } - const sym = checker.getSymbolAtLocation(node); + const sym = resolveSymbol(checker, node); if (sym) onReference(sym, isValidTypeOnlyAliasUseSite(node)); } else { @@ -976,6 +978,11 @@ function forEachReference(node: Node, checker: TypeChecker, enclosingRange: Text }); } +function resolveSymbol(checker: TypeChecker, node: Identifier) { + const symbol = checker.getSymbolAtLocation(node); + return symbol === undefined || isTransientSymbol(symbol) ? checker.resolveName(idText(node), node, SymbolFlags.All, /*excludeGlobals*/ false) : symbol; +} + function forEachTopLevelDeclaration(statement: Statement, cb: (node: TopLevelDeclaration) => T): T | undefined { switch (statement.kind) { case SyntaxKind.FunctionDeclaration: diff --git a/tests/cases/fourslash/moveToNewFile_namespaceExport.ts b/tests/cases/fourslash/moveToNewFile_namespaceExport.ts new file mode 100644 index 0000000000000..175a977b7162a --- /dev/null +++ b/tests/cases/fourslash/moveToNewFile_namespaceExport.ts @@ -0,0 +1,25 @@ +/// + +// @module: esnext + +// @filename: /a.ts +////export interface A {} + +// @filename: /b.ts +////export * as A from "./a"; +////export type B = string + +// @filename: /c.ts +////import { A } from "./b" +////[|type B = A.B|] + +verify.moveToNewFile({ + newFileContents: { + "/c.ts": '', + "/B.1.ts": +`import { A } from "./a"; + +type B = A.B; +`, + }, +});