diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 603508d6c7d..64389726ebc 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -372,7 +372,8 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu quotePreference, defaultImport, namedImports && arrayFrom(namedImports.entries(), ([name, addAsTypeOnly]) => ({ addAsTypeOnly, name })), - namespaceLikeImport); + namespaceLikeImport, + compilerOptions); newDeclarations = combine(newDeclarations, declarations); }); if (newDeclarations) { @@ -1249,7 +1250,13 @@ function codeActionForFixWorker(changes: textChanges.ChangeTracker, sourceFile: const namespaceLikeImport = importKind === ImportKind.Namespace || importKind === ImportKind.CommonJS ? { importKind, name: qualification?.namespacePrefix || symbolName, addAsTypeOnly } : undefined; - insertImports(changes, sourceFile, getDeclarations(moduleSpecifier, quotePreference, defaultImport, namedImports, namespaceLikeImport), /*blankLineBetween*/ true, preferences); + insertImports(changes, sourceFile, getDeclarations( + moduleSpecifier, + quotePreference, + defaultImport, + namedImports, + namespaceLikeImport, + compilerOptions), /*blankLineBetween*/ true, preferences); if (qualification) { addNamespaceQualifier(changes, sourceFile, qualification); } @@ -1489,12 +1496,18 @@ function getNewImports( quotePreference: QuotePreference, defaultImport: Import | undefined, namedImports: readonly Import[] | undefined, - namespaceLikeImport: Import & { importKind: ImportKind.CommonJS | ImportKind.Namespace } | undefined + namespaceLikeImport: Import & { importKind: ImportKind.CommonJS | ImportKind.Namespace } | undefined, + compilerOptions: CompilerOptions, ): AnyImportSyntax | readonly AnyImportSyntax[] { const quotedModuleSpecifier = makeStringLiteral(moduleSpecifier, quotePreference); let statements: AnyImportSyntax | readonly AnyImportSyntax[] | undefined; if (defaultImport !== undefined || namedImports?.length) { - const topLevelTypeOnly = (!defaultImport || needsTypeOnly(defaultImport)) && every(namedImports, needsTypeOnly); + // `verbatimModuleSyntax` should prefer top-level `import type` - + // even though it's not an error, it would add unnecessary runtime emit. + const topLevelTypeOnly = (!defaultImport || needsTypeOnly(defaultImport)) && every(namedImports, needsTypeOnly) || + compilerOptions.verbatimModuleSyntax && + defaultImport?.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && + !some(namedImports, i => i.addAsTypeOnly === AddAsTypeOnly.NotAllowed); statements = combine(statements, makeImport( defaultImport && factory.createIdentifier(defaultImport.name), namedImports?.map(({ addAsTypeOnly, name }) => factory.createImportSpecifier( diff --git a/tests/cases/fourslash/autoImportTypeOnlyPreferred1.ts b/tests/cases/fourslash/autoImportTypeOnlyPreferred1.ts new file mode 100644 index 00000000000..502e9735187 --- /dev/null +++ b/tests/cases/fourslash/autoImportTypeOnlyPreferred1.ts @@ -0,0 +1,43 @@ +/// + +// @verbatimModuleSyntax: true +// @module: esnext +// @moduleResolution: bundler + +// @Filename: /ts.d.ts +//// declare namespace ts { +//// interface SourceFile { +//// text: string; +//// } +//// function createSourceFile(): SourceFile; +//// } +//// export = ts; + +// @Filename: /types.ts +//// export interface VFS { +//// getSourceFile(path: string): ts/**/ +//// } + +verify.completions({ + marker: "", + includes: [{ + name: "ts", + source: "./ts", + sourceDisplay: "./ts", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions, + }], + preferences: { + includeCompletionsForModuleExports: true, + allowIncompleteCompletions: true, + }, +}).andApplyCodeAction({ + name: "ts", + source: "./ts", + description: `Add import from "./ts"`, + newFileContent: `import type ts from "./ts"; + +export interface VFS { + getSourceFile(path: string): ts +}` +});