diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 6034c9dfc3de1..e04b2b0f5250a 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -1098,22 +1098,28 @@ function getAddAsTypeOnly( return AddAsTypeOnly.Allowed; } -function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { - let best: FixAddToExistingImport | undefined; - for (const existingImport of existingImports) { - const fix = getAddToExistingImportFix(existingImport); - if (!fix) continue; - const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); - if ( - fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || - fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly - ) { - // Give preference to putting types in existing type-only imports and avoiding conversions - // of import statements to/from type-only. - return fix; - } - best ??= fix; - } +function tryAddToExistingImport(existingImports: readonly FixAddToExistingImportInfo[], isValidTypeOnlyUseSite: boolean, checker: TypeChecker, compilerOptions: CompilerOptions): FixAddToExistingImport | undefined { + let best: FixAddToExistingImport | undefined; + for (const existingImport of existingImports) { + const fix = getAddToExistingImportFix(existingImport); + if (!fix) continue; + const isTypeOnly = isTypeOnlyImportDeclaration(fix.importClauseOrBindingPattern); + + // Don't add value-only symbols to type-only imports + if (fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && isTypeOnly) { + continue; + } + + if ( + fix.addAsTypeOnly !== AddAsTypeOnly.NotAllowed && isTypeOnly || + fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && !isTypeOnly + ) { + // Give preference to putting types in existing type-only imports and avoiding conversions + // of import statements to/from type-only. + return fix; + } + best ??= fix; + } return best; function getAddToExistingImportFix({ declaration, importKind, symbol, targetFlags }: FixAddToExistingImportInfo): FixAddToExistingImport | undefined { diff --git a/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts new file mode 100644 index 0000000000000..b4e175776c889 --- /dev/null +++ b/tests/cases/fourslash/completionsSymbolTypeOnlyImportBug.ts @@ -0,0 +1,47 @@ +/// + +// Test case for symbol property auto-import issue +// Should import symbol as value, not add to existing type-only import + +// @Filename: /exportsSymbol.ts +////export const SYM_FOO_BAR = Symbol.for("foo.bar"); +//// +////export interface ObjWithSym { +//// [SYM_FOO_BAR]: any; +////} + +// @Filename: /usesSymbol.ts +////import type { ObjWithSym } from "./exportsSymbol"; +//// +////export declare const thing: ObjWithSym; +//// +////function main() { +//// thing[/**/] +////} + +goTo.marker(""); + +verify.completions({ + includes: [ + { name: "SYM_FOO_BAR", source: "/exportsSymbol", hasAction: true }, + ], + preferences: { + includeCompletionsForModuleExports: true, + }, +}); + +// This should create a separate value import, not add to the existing type-only import +verify.applyCodeActionFromCompletion("", { + name: "SYM_FOO_BAR", + source: "/exportsSymbol", + description: `Add import from "./exportsSymbol"`, + newFileContent: +`import type { ObjWithSym } from "./exportsSymbol"; +import { SYM_FOO_BAR } from "./exportsSymbol"; + +export declare const thing: ObjWithSym; + +function main() { + thing[SYM_FOO_BAR] +}` +}); \ No newline at end of file