diff --git a/.changeset/angry-parts-change.md b/.changeset/angry-parts-change.md new file mode 100644 index 000000000000..2580c2277632 --- /dev/null +++ b/.changeset/angry-parts-change.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#8759](https://github.com/biomejs/biome/issues/8759): The [`useConsistentTypeDefinitions`](https://biomejs.dev/linter/rules/use-consistent-type-definitions/) rule no longer converts empty object type declarations into interfaces, as it will conflict with the [`noEmptyInterface`](https://biomejs.dev/linter/rules/no-empty-interface/) rule and can cause an infinite loop when both rules are enabled. diff --git a/crates/biome_js_analyze/src/lint/style/use_consistent_type_definitions.rs b/crates/biome_js_analyze/src/lint/style/use_consistent_type_definitions.rs index b64e84c3d5a2..fc04f3c8af75 100644 --- a/crates/biome_js_analyze/src/lint/style/use_consistent_type_definitions.rs +++ b/crates/biome_js_analyze/src/lint/style/use_consistent_type_definitions.rs @@ -23,6 +23,9 @@ declare_lint_rule! { /// Consistent type definition styles, aside from improving code readability, help minimize cognitive load when developers /// switch between different codebases or within a large codebase. /// + /// Empty object type declarations will be left as-is and will not be converted to interfaces, + /// as it will conflict with the `noEmptyInterface` rule. + /// /// ## Example /// /// ### Invalid @@ -40,6 +43,10 @@ declare_lint_rule! { /// } /// ``` /// + /// ```ts + /// type AnyObject = {}; + /// ``` + /// /// ## Options /// /// The following options are available @@ -180,8 +187,16 @@ fn can_convert_interface_to_type(interface_decl: &TsInterfaceDeclaration) -> boo fn can_convert_type_to_interface(type_alias: &TsTypeAliasDeclaration) -> bool { // Check if the type alias has type parameters // Type aliases with complex types cannot be converted to interfaces + let Ok(AnyTsType::TsObjectType(ty)) = type_alias.ty() else { + return false; + }; - matches!(type_alias.ty(), Ok(AnyTsType::TsObjectType(_))) + // Converting empty types into interfaces will conflict with the `noEmptyInterface` rule + if ty.members().is_empty() { + return false; + } + + true } fn convert_interface_to_type_alias( diff --git a/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts b/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts index 5ab08fe18051..5a3e3f5331ec 100644 --- a/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts +++ b/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts @@ -38,4 +38,7 @@ type Nullable = T | null; // Interfaces can extend interface ExtendedInterface extends Foo { extra: boolean; -} \ No newline at end of file +} + +// Empty types will be left as-is +type AnyObject = {}; diff --git a/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts.snap b/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts.snap index 85c55a0c8fe8..8a4521feef7c 100644 --- a/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts.snap +++ b/crates/biome_js_analyze/tests/specs/style/useConsistentTypeDefinitions/valid.ts.snap @@ -45,4 +45,8 @@ type Nullable = T | null; interface ExtendedInterface extends Foo { extra: boolean; } + +// Empty types will be left as-is +type AnyObject = {}; + ```