Skip to content
Closed
19 changes: 10 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@
"fast-xml-parser": "^4.5.2",
"glob": "^10.4.5",
"globals": "^15.15.0",
"hereby": "^1.10.0",
"hereby": "^1.11.0",
"jsonc-parser": "^3.3.1",
"knip": "^5.44.4",
"minimist": "^1.2.8",
44 changes: 26 additions & 18 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
@@ -1098,22 +1098,30 @@ 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);

// Skip incompatible combinations: value imports going to type-only imports
if (fix.addAsTypeOnly === AddAsTypeOnly.NotAllowed && isTypeOnly) {
continue;
}

// Perfect matches: return immediately
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 {
@@ -1902,8 +1910,8 @@ function doAddExistingFix(
return;
}

// promoteFromTypeOnly = true if we need to promote the entire original clause from type only
const promoteFromTypeOnly = clause.isTypeOnly && some([defaultImport, ...namedImports], i => i?.addAsTypeOnly === AddAsTypeOnly.NotAllowed);
// promoteFromTypeOnly = true if we need to promote the entire original clause from type only
const promoteFromTypeOnly = clause.isTypeOnly && some([defaultImport, ...namedImports], i => i?.addAsTypeOnly === AddAsTypeOnly.NotAllowed);
const existingSpecifiers = clause.namedBindings && tryCast(clause.namedBindings, isNamedImports)?.elements;

if (defaultImport) {
34 changes: 34 additions & 0 deletions tests/cases/fourslash/completionsSymbolImportAsValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/// <reference path="fourslash.ts" />

// Test the simplest case possible

// @Filename: /exports.ts
////export const VALUE = 42;
////export interface SomeType { }

// @Filename: /imports.ts
////import type { SomeType } from "./exports";
////function main() {
//// /*completion*/;
////}

verify.completions({
marker: "completion",
includes: [
{ name: "VALUE", source: "/exports", hasAction: true, sortText: "16" },
],
preferences: {
includeCompletionsForModuleExports: true,
},
});

verify.applyCodeActionFromCompletion("completion", {
name: "VALUE",
source: "/exports",
description: `Update import from "./exports"`,
newFileContent:
`import { VALUE, type SomeType } from "./exports";
function main() {
VALUE;
}`
});