Skip to content

Commit 292d018

Browse files
authored
Add code fix for importsNotUsedAsValues error (#37468)
1 parent e15a9fb commit 292d018

File tree

5 files changed

+155
-0
lines changed

5 files changed

+155
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
const errorCodes = [Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error.code];
4+
const fixId = "convertToTypeOnlyImport";
5+
registerCodeFix({
6+
errorCodes,
7+
getCodeActions: context => {
8+
const changes = textChanges.ChangeTracker.with(context, t => {
9+
const importDeclaration = getImportDeclarationForDiagnosticSpan(context.span, context.sourceFile);
10+
fixSingleImportDeclaration(t, importDeclaration, context);
11+
});
12+
if (changes.length) {
13+
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_type_only_import, fixId, Diagnostics.Convert_all_imports_not_used_as_a_value_to_type_only_imports)];
14+
}
15+
},
16+
fixIds: [fixId],
17+
getAllCodeActions: context => {
18+
return codeFixAll(context, errorCodes, (changes, diag) => {
19+
const importDeclaration = getImportDeclarationForDiagnosticSpan(diag, context.sourceFile);
20+
fixSingleImportDeclaration(changes, importDeclaration, context);
21+
});
22+
}
23+
});
24+
25+
function getImportDeclarationForDiagnosticSpan(span: TextSpan, sourceFile: SourceFile) {
26+
return tryCast(getTokenAtPosition(sourceFile, span.start).parent, isImportDeclaration);
27+
}
28+
29+
function fixSingleImportDeclaration(changes: textChanges.ChangeTracker, importDeclaration: ImportDeclaration | undefined, context: CodeFixContextBase) {
30+
if (!importDeclaration?.importClause) {
31+
return;
32+
}
33+
34+
const { importClause } = importDeclaration;
35+
// `changes.insertModifierBefore` produces a range that might overlap further changes
36+
changes.insertText(context.sourceFile, importDeclaration.getStart() + "import".length, " type");
37+
38+
// `import type foo, { Bar }` is not allowed, so move `foo` to new declaration
39+
if (importClause.name && importClause.namedBindings) {
40+
changes.deleteNodeRangeExcludingEnd(context.sourceFile, importClause.name, importDeclaration.importClause.namedBindings);
41+
changes.insertNodeBefore(context.sourceFile, importDeclaration, updateImportDeclaration(
42+
importDeclaration,
43+
/*decorators*/ undefined,
44+
/*modifiers*/ undefined,
45+
createImportClause(
46+
importClause.name,
47+
/*namedBindings*/ undefined,
48+
/*isTypeOnly*/ true),
49+
importDeclaration.moduleSpecifier));
50+
}
51+
}
52+
}

src/services/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"codefixes/convertToEs6Module.ts",
6161
"codefixes/correctQualifiedNameToIndexedAccessType.ts",
6262
"codefixes/convertToTypeOnlyExport.ts",
63+
"codefixes/convertToTypeOnlyImport.ts",
6364
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
6465
"codefixes/importFixes.ts",
6566
"codefixes/fixImplicitThis.ts",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @importsNotUsedAsValues: error
4+
5+
// @Filename: exports.ts
6+
////export default class A {}
7+
////export class B {}
8+
////export class C {}
9+
10+
// @Filename: imports.ts
11+
////import {
12+
//// B,
13+
//// C,
14+
////} from './exports';
15+
////
16+
////declare const b: B;
17+
////declare const c: C;
18+
////console.log(b, c);
19+
20+
goTo.file("imports.ts");
21+
verify.codeFix({
22+
index: 0,
23+
description: ts.Diagnostics.Convert_to_type_only_import.message,
24+
newFileContent: `import type {
25+
B,
26+
C,
27+
} from './exports';
28+
29+
declare const b: B;
30+
declare const c: C;
31+
console.log(b, c);`
32+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @importsNotUsedAsValues: error
4+
5+
// @Filename: exports.ts
6+
////export default class A {}
7+
////export class B {}
8+
////export class C {}
9+
10+
// @Filename: imports.ts
11+
////import A, { B, C } from './exports';
12+
////
13+
////declare const a: A;
14+
////declare const b: B;
15+
////declare const c: C;
16+
////console.log(a, b, c);
17+
18+
goTo.file("imports.ts");
19+
verify.codeFix({
20+
index: 0,
21+
description: ts.Diagnostics.Convert_to_type_only_import.message,
22+
newFileContent: `import type A from './exports';
23+
import type { B, C } from './exports';
24+
25+
declare const a: A;
26+
declare const b: B;
27+
declare const c: C;
28+
console.log(a, b, c);`
29+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @importsNotUsedAsValues: error
4+
5+
// @Filename: exports1.ts
6+
////export default class A {}
7+
////export class B {}
8+
////export class C {}
9+
10+
// @Filename: exports2.ts
11+
////export default class D {}
12+
////export class E {}
13+
////export class F {}
14+
15+
// @Filename: imports.ts
16+
////import A, { B, C } from './exports1';
17+
////import D, * as others from "./exports2";
18+
////
19+
////declare const a: A;
20+
////declare const b: B;
21+
////declare const c: C;
22+
////declare const d: D;
23+
////declare const o: typeof others;
24+
////console.log(a, b, c, d, o);
25+
26+
goTo.file("imports.ts");
27+
verify.codeFixAll({
28+
fixId: "convertToTypeOnlyImport",
29+
fixAllDescription: ts.Diagnostics.Convert_all_imports_not_used_as_a_value_to_type_only_imports.message,
30+
newFileContent: `import type A from './exports1';
31+
import type { B, C } from './exports1';
32+
import type D from "./exports2";
33+
import type * as others from "./exports2";
34+
35+
declare const a: A;
36+
declare const b: B;
37+
declare const c: C;
38+
declare const d: D;
39+
declare const o: typeof others;
40+
console.log(a, b, c, d, o);`
41+
});

0 commit comments

Comments
 (0)