Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/chatty-ravens-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@biomejs/biome": patch
---

Fixed [#7289](https://github.com/biomejs/biome/issues/7289). The rule [`useImportType`](https://biomejs.dev/linter/rules/use-import-type/) now inlines `import type` into `import { type }` when the `style` option is set to `inlineType`.

Example:

```ts
import type { T } from "mod";
// becomes
import { type T } from "mod";
```
59 changes: 46 additions & 13 deletions crates/biome_js_analyze/src/lint/style/use_import_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ impl Rule for UseImportType {
}
let import = ctx.query();
let import_clause = import.import_clause().ok()?;
let extension = ctx.file_path().extension()?;
let extension = extension.as_bytes();
// Import attributes and type-only imports are not compatible in ESM.
if import_clause.attribute().is_some()
&& extension != b"cts"
&& ctx
.file_path()
.extension()
.is_none_or(|extension| extension != "cts")
&& !matches!(ctx.root(), AnyJsRoot::JsScript(_))
{
return None;
Expand Down Expand Up @@ -286,10 +287,28 @@ impl Rule for UseImportType {
is_only_used_as_type(model, default_binding).then_some(ImportTypeFix::UseImportType)
}
AnyJsImportClause::JsImportNamedClause(clause) => {
let type_token = clause.type_token();
if style == Style::InlineType && type_token.is_some() {
// Inline `import type` into `import { type }`
let specifiers = clause
.named_specifiers()
.ok()?
.specifiers()
.iter()
.collect::<Result<Vec<_>, _>>()
.ok()?;
return if specifiers.is_empty() {
None
} else {
Some(ImportTypeFix::AddTypeQualifiers(
specifiers.into_boxed_slice(),
))
};
}
match named_import_type_fix(
model,
&clause.named_specifiers().ok()?,
clause.type_token().is_some(),
type_token.is_some(),
)? {
NamedImportTypeFix::UseImportType(specifiers) => {
if style == Style::InlineType {
Expand Down Expand Up @@ -394,16 +413,26 @@ impl Rule for UseImportType {
}
}
ImportTypeFix::AddTypeQualifiers(named_specifiers) => {
let mut diagnostic = RuleDiagnostic::new(
rule_category!(),
import_clause.range(),
"Some named imports are only used as types.",
);
for specifier in named_specifiers {
diagnostic =
diagnostic.detail(specifier.range(), "This import is only used as a type.")
if import_clause.type_token().is_some() {
RuleDiagnostic::new(
rule_category!(),
import_clause.range(),
markup! {
"Use "<Emphasis>"import { type }"</Emphasis>" instead of "<Emphasis>"import type"</Emphasis>"."
},
)
} else {
let mut diagnostic = RuleDiagnostic::new(
rule_category!(),
import_clause.range(),
"Some named imports are only used as types.",
);
for specifier in named_specifiers {
diagnostic = diagnostic
.detail(specifier.range(), "This import is only used as a type.")
}
diagnostic
}
diagnostic
}
ImportTypeFix::RemoveTypeQualifiers(type_tokens) => {
let mut diagnostic = RuleDiagnostic::new(
Expand Down Expand Up @@ -727,6 +756,10 @@ impl Rule for UseImportType {
}
}
ImportTypeFix::AddTypeQualifiers(specifiers) => {
if let Some(type_token) = import_clause.type_token() {
// Inline `import type` into `import { type }`
mutation.remove_token(type_token);
}
for specifier in specifiers {
let new_specifier = specifier
.clone()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ export { V3, V4 };
import V5, { T9 } from "mod";
export type { T9 };
export { V5 };

import type { T10 } from "mod";
export type { T10 };
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import V5, { T9 } from "mod";
export type { T9 };
export { V5 };

import type { T10 } from "mod";
export type { T10 };

```

# Diagnostics
Expand Down Expand Up @@ -177,3 +180,29 @@ invalid-inline-type.ts:16:8 lint/style/useImportType FIXABLE ━━━━━
│ +++++

```

```
invalid-inline-type.ts:20:8 lint/style/useImportType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Use import { type } instead of import type.

18 │ export { V5 };
19 │
> 20 │ import type { T10 } from "mod";
│ ^^^^^^^^^^^^^^^^^^^^^^^
21 │ export type { T10 };
22 │

i Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.

i Safe fix: Add inline type keywords.

18 18 │ export { V5 };
19 19 │
20 │ - import·type·{·T10·}·from·"mod";
20 │ + import·{·type·T10·}·from·"mod";
21 21 │ export type { T10 };
22 22 │


```
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ export type { T1, T2 };
import V1, { type T3 } from "mod";
export type { T3 };
export { V1 };

// Edge case
import type {} from "mod";

Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ import V1, { type T3 } from "mod";
export type { T3 };
export { V1 };

// Edge case
import type {} from "mod";


```
Loading