diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 211bdec7c654..0ca46399053b 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -110,6 +110,7 @@ define_categories! { "lint/nursery/useGroupedTypeImport": "https://biomejs.dev/linter/rules/use-grouped-type-import", "lint/nursery/useImportRestrictions": "https://biomejs.dev/linter/rules/use-import-restrictions", "lint/nursery/useShorthandAssign": "https://biomejs.dev/lint/rules/use-shorthand-assign", + "lint/nursery/useShorthandFunctionType": "https://biomejs.dev/lint/rules/use-shorthand-function-type", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noDelete": "https://biomejs.dev/linter/rules/no-delete", "lint/security/noDangerouslySetInnerHtml": "https://biomejs.dev/linter/rules/no-dangerously-set-inner-html", diff --git a/crates/biome_js_analyze/src/analyzers/nursery.rs b/crates/biome_js_analyze/src/analyzers/nursery.rs index 4890cc39b436..698798bea743 100644 --- a/crates/biome_js_analyze/src/analyzers/nursery.rs +++ b/crates/biome_js_analyze/src/analyzers/nursery.rs @@ -14,6 +14,7 @@ pub(crate) mod use_as_const_assertion; pub(crate) mod use_grouped_type_import; pub(crate) mod use_import_restrictions; pub(crate) mod use_shorthand_assign; +pub(crate) mod use_shorthand_function_type; declare_group! { pub (crate) Nursery { @@ -31,6 +32,7 @@ declare_group! { self :: use_grouped_type_import :: UseGroupedTypeImport , self :: use_import_restrictions :: UseImportRestrictions , self :: use_shorthand_assign :: UseShorthandAssign , + self :: use_shorthand_function_type :: UseShorthandFunctionType , ] } } diff --git a/crates/biome_js_analyze/src/analyzers/nursery/use_shorthand_function_type.rs b/crates/biome_js_analyze/src/analyzers/nursery/use_shorthand_function_type.rs new file mode 100644 index 000000000000..add6dcf2c2de --- /dev/null +++ b/crates/biome_js_analyze/src/analyzers/nursery/use_shorthand_function_type.rs @@ -0,0 +1,276 @@ +use crate::semantic_services::Semantic; +use crate::JsRuleAction; +use biome_analyze::{ + context::RuleContext, declare_rule, ActionCategory, Rule, RuleAction, RuleDiagnostic, +}; +use biome_console::markup; +use biome_diagnostics::Applicability; +use biome_js_factory::make; +use biome_js_syntax::{AnyJsDeclarationClause, AnyTsReturnType, AnyTsType, JsParameterList, JsFormalParameter, JsParameters, JsSyntaxToken, TsFunctionType, TsInterfaceDeclaration, TsTypeAliasDeclaration, T, TsTypeParameters, TsIdentifierBinding, AnyJsFormalParameter, AnyJsExpression}; +use biome_js_syntax::AnyTsType::TsObjectType; +use biome_js_syntax::AnyTsTypeMember::TsCallSignatureTypeMember; +use biome_js_syntax::parameter_ext::AnyParameter::AnyJsParameter; +use biome_rowan::{ + declare_node_union, AstNode, AstNodeExt, AstNodeList, BatchMutationExt, SyntaxResult, + TextRange, TriviaPieceKind, +}; + +declare_rule! { + /// TypeScript allows for two common ways to declare a type for a function: + /// + /// - Function type: `() => string` + /// - Object type with a signature: `{ (): string }` + /// + /// The function type form is generally preferred when possible for being more succinct. + /// + /// This rule suggests using a function type instead of an interface or object type literal with a single call signature. + /// + /// Source: https://typescript-eslint.io/rules/prefer-function-type/ + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```ts,expect_diagnostic + /// interface Example { + /// (): string; + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// function foo(example: { (): number }): number { + /// return example(); + /// } + /// ``` + /// + /// ```ts,expect_diagnostic + /// interface ReturnsSelf { + /// // returns the function itself, not the `this` argument. + /// (arg: string): this; + /// } + /// ``` + /// + /// ## Valid + /// + /// ```ts + /// type Example = () => string; + /// ``` + /// + /// ```ts + /// function foo(example: () => number): number { + /// return bar(); + /// } + /// ``` + /// + /// ```ts + /// // returns the function itself, not the `this` argument. + /// type ReturnsSelf2 = (arg: string) => ReturnsSelf; + /// ``` + /// + /// ```ts + /// interface Foo { + /// bar: string; + /// } + /// interface Bar extends Foo { + /// (): void; + /// } + /// ``` + /// + /// ```ts + /// // multiple call signatures (overloads) is allowed: + /// interface Overloaded { + /// (data: string): number; + /// (id: number): string; + /// } + /// // this is equivelent to Overloaded interface. + /// type Intersection = ((data: string) => number) & ((id: number) => string); + ///``` + /// + pub(crate) UseShorthandFunctionType { + version: "1.3.0", + name: "useShorthandFunctionType", + recommended: false, + } +} + +declare_node_union! { + pub(crate) Query = TsInterfaceDeclaration | JsFormalParameter +} + +pub(crate) enum RuleState { + TsInterfaceDeclaration(TextRange, JsParameterList, SyntaxResult), + TsObjectType(TextRange, JsParameterList, SyntaxResult), +} + +impl Rule for UseShorthandFunctionType { + type Query = Semantic; + type State = RuleState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let query = ctx.query(); + + match query { + Query::TsInterfaceDeclaration(interface_declaration) => { + if interface_declaration.members().len() == 1 + && interface_declaration.extends_clause().is_none() + { + if let Some(TsCallSignatureTypeMember(call_signature)) = + interface_declaration.members().first() + { + return Some(RuleState::TsInterfaceDeclaration( + query.range(), + call_signature.parameters().ok()?.items(), + call_signature.return_type_annotation()?.ty(), + )); + } + } + None + } + Query::JsFormalParameter(parameter) => { + if let Some(TsObjectType(ts_object)) = parameter.type_annotation()?.ty().ok() { + if ts_object.members().len() == 1 { + if let Some(ts_call_signature_type_member) = ts_object + .members() + .first()? + .as_ts_call_signature_type_member() + { + return Some(RuleState::TsObjectType( + ts_object.range(), + ts_call_signature_type_member.parameters().ok()?.items(), + ts_call_signature_type_member + .return_type_annotation()?.ty(), + )); + } + } + } + None + } + } + } + + fn diagnostic(_: &RuleContext, state: &Self::State) -> Option { + let (range, message, note) = match state { + RuleState::TsInterfaceDeclaration(range, parameters, return_type_syntax) => ( + range, + markup! { + "Prefer function type over interface." + }, + markup! {"Interface only has a call signature, you should use a function type instead."}, + ), + RuleState::TsObjectType(range, parameters, return_type_syntax) => ( + range, + markup! { + "Prefer function type over object type." + }, + markup! {"Object only has a call signature, you should use a function type instead."}, + ), + }; + Some(RuleDiagnostic::new(rule_category!(), range, message).note(note)) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + let mut mutation = ctx.root().begin(); + + match node { + Query::TsInterfaceDeclaration(interface_declaration) => { + let ts_call_signature_type_member = interface_declaration.members().first()?.as_ts_call_signature_type_member()?.clone(); + + let new_type_alias = create_type_alias( + interface_declaration.id().ok()?, + interface_declaration.type_parameters(), + AnyTsType::from(make::ts_function_type( + make::js_parameters( + make::token(T!['(']), + ts_call_signature_type_member.parameters().ok()?.items(), + make::token(T![')']).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ), + make::token(T![=>]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ts_call_signature_type_member.return_type_annotation()?.ty().ok()?, + ).build()) + ); + + mutation.replace_node( + AnyJsDeclarationClause::from(interface_declaration.clone()), + AnyJsDeclarationClause::from(new_type_alias?), + ); + + return Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::Always, + message: markup! { "Convert empty interface to type alias." }.to_owned(), + mutation, + }); + } + Query::JsFormalParameter(formal_parameter) => { + let ts_call_signature_type_member = formal_parameter.type_annotation()?.ty().ok()?.as_ts_object_type()?.members().first()?.as_ts_call_signature_type_member()?.clone(); + + let function_type = make::ts_function_type( + make::js_parameters( + make::token(T!['(']), + ts_call_signature_type_member.parameters().ok()?.items(), + make::token(T![')']).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ), + make::token(T![=>]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ts_call_signature_type_member.return_type_annotation()?.ty().ok()?, + ).build(); + + let new_type_annotation = make::ts_type_annotation( + make::token(T![:]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + AnyTsType::from(function_type), + ); + + mutation.replace_node( + formal_parameter.type_annotation()?, + new_type_annotation, + ); + + return Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::Always, + message: markup! { "Convert object type signature to function type." }.to_owned(), + mutation, + }); + }, + } + } +} +fn create_type_alias( + id: TsIdentifierBinding, + type_params: Option, + ts_type: AnyTsType, +) -> Option { + let new_node = make::ts_type_alias_declaration( + make::token(T![type]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + id, + make::token(T![=]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ts_type, + ); + + let new_node = if type_params.is_some() { + new_node.with_type_parameters(type_params?).build() + } else { + new_node.build() + }; + + Some(new_node) +} + +fn create_function_type_parameter( + type_params: JsParameterList, + ts_type: AnyTsType, +) -> Option { + let new_node = make::ts_function_type( + make::js_parameters( + make::token(T!['(']), + type_params, + make::token(T![')']).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + ), + make::token(T![=>]).with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]), + AnyTsReturnType::AnyTsType(ts_type), + ).build(); + + Some(new_node) +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts new file mode 100644 index 000000000000..5477e3725eee --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts @@ -0,0 +1,12 @@ +interface Example { + (): string; +} + +function foo(example: { (): number }): number { + return example(); +} + +interface ReturnsSelf { + // returns the function itself, not the `this` argument. + (arg: string): this; +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts.snap.new b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts.snap.new new file mode 100644 index 000000000000..f4db34245a3e --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/invalid.ts.snap.new @@ -0,0 +1,105 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 83 +expression: invalid.ts +--- +# Input +```js +interface Example { + (): string; +} + +function foo(example: { (): number }): number { + return example(); +} + +interface ReturnsSelf { + // returns the function itself, not the `this` argument. + (arg: string): this; +} +``` + +# Diagnostics +``` +invalid.ts:1:1 lint/nursery/useShorthandFunctionType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prefer function type over interface. + + > 1 │ interface Example { + │ ^^^^^^^^^^^^^^^^^^^ + > 2 │ (): string; + > 3 │ } + │ ^ + 4 │ + 5 │ function foo(example: { (): number }): number { + + i Interface only has a call signature, you should use a function type instead. + + i Safe fix: Convert empty interface to type alias. + + 1 │ - interface·Example·{ + 2 │ - ·():·string; + 3 │ - } + 1 │ + type·Example·=·()·=>·string + 4 2 │ + 5 3 │ function foo(example: { (): number }): number { + + +``` + +``` +invalid.ts:5:23 lint/nursery/useShorthandFunctionType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prefer function type over object type. + + 3 │ } + 4 │ + > 5 │ function foo(example: { (): number }): number { + │ ^^^^^^^^^^^^^^ + 6 │ return example(); + 7 │ } + + i Object only has a call signature, you should use a function type instead. + + i Safe fix: Convert empty interface to type alias. + + 3 3 │ } + 4 4 │ + 5 │ - function·foo(example:·{·():·number·}):·number·{ + 5 │ + function·foo(example:·()·=>·number):·number·{ + 6 6 │ return example(); + 7 7 │ } + + +``` + +``` +invalid.ts:9:1 lint/nursery/useShorthandFunctionType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Prefer function type over interface. + + 7 │ } + 8 │ + > 9 │ interface ReturnsSelf { + │ ^^^^^^^^^^^^^^^^^^^^^^^ + > 10 │ // returns the function itself, not the `this` argument. + > 11 │ (arg: string): this; + > 12 │ } + │ ^ + + i Interface only has a call signature, you should use a function type instead. + + i Safe fix: Convert empty interface to type alias. + + 7 7 │ } + 8 8 │ + 9 │ - interface·ReturnsSelf·{ + 10 │ - ·//·returns·the·function·itself,·not·the·`this`·argument. + 11 │ - ·(arg:·string):·this; + 12 │ - } + 9 │ + type·ReturnsSelf·=·(arg:·string)·=>·this + + +``` + + diff --git a/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts new file mode 100644 index 000000000000..b449c79cabf9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts @@ -0,0 +1,23 @@ +type Example = () => string; + +function foo(example: () => number): number { + return bar(); +} + +// returns the function itself, not the `this` argument. +type ReturnsSelf = (arg: string) => ReturnsSelf; + +interface Foo { + bar: string; +} +interface Bar extends Foo { + (): void; +} + +// multiple call signatures (overloads) is allowed: +interface Overloaded { + (data: string): number; + (id: number): string; +} +// this is equivelent to Overloaded interface. +type Intersection = ((data: string) => number) & ((id: number) => string); \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts.snap.new b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts.snap.new new file mode 100644 index 000000000000..3bbd5998846d --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useShorthandFunctionType/valid.ts.snap.new @@ -0,0 +1,33 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +assertion_line: 83 +expression: valid.ts +--- +# Input +```js +type Example = () => string; + +function foo(example: () => number): number { + return bar(); +} + +// returns the function itself, not the `this` argument. +type ReturnsSelf = (arg: string) => ReturnsSelf; + +interface Foo { + bar: string; +} +interface Bar extends Foo { + (): void; +} + +// multiple call signatures (overloads) is allowed: +interface Overloaded { + (data: string): number; + (id: number): string; +} +// this is equivelent to Overloaded interface. +type Intersection = ((data: string) => number) & ((id: number) => string); +``` + + diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 301939fc2cef..ee944bf58479 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2363,10 +2363,19 @@ pub struct Nursery { #[bpaf(long("use-shorthand-assign"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] pub use_shorthand_assign: Option, + #[doc = "Succinct description of the rule."] + #[bpaf( + long("use-shorthand-function-type"), + argument("on|off|warn"), + optional, + hide + )] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_shorthand_function_type: Option, } impl Nursery { const GROUP_NAME: &'static str = "nursery"; - pub(crate) const GROUP_RULES: [&'static str; 17] = [ + pub(crate) const GROUP_RULES: [&'static str; 18] = [ "noApproximativeNumericConstant", "noDuplicateJsonKeys", "noEmptyBlockStatements", @@ -2384,6 +2393,7 @@ impl Nursery { "useGroupedTypeImport", "useImportRestrictions", "useShorthandAssign", + "useShorthandFunctionType", ]; const RECOMMENDED_RULES: [&'static str; 8] = [ "noDuplicateJsonKeys", @@ -2405,7 +2415,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), ]; - const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 17] = [ + const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 18] = [ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[1]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]), @@ -2423,6 +2433,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended(&self) -> bool { @@ -2524,6 +2535,11 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } + if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -2613,6 +2629,11 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } + if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -2626,7 +2647,7 @@ impl Nursery { pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 8] { Self::RECOMMENDED_RULES_AS_FILTERS } - pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 17] { + pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 18] { Self::ALL_RULES_AS_FILTERS } #[doc = r" Select preset rules"] @@ -2670,6 +2691,7 @@ impl Nursery { "useGroupedTypeImport" => self.use_grouped_type_import.as_ref(), "useImportRestrictions" => self.use_import_restrictions.as_ref(), "useShorthandAssign" => self.use_shorthand_assign.as_ref(), + "useShorthandFunctionType" => self.use_shorthand_function_type.as_ref(), _ => None, } } diff --git a/crates/biome_service/src/configuration/parse/json/rules.rs b/crates/biome_service/src/configuration/parse/json/rules.rs index 4b7819c19d57..0a509a2dcf5e 100644 --- a/crates/biome_service/src/configuration/parse/json/rules.rs +++ b/crates/biome_service/src/configuration/parse/json/rules.rs @@ -2136,6 +2136,7 @@ impl VisitNode for Nursery { "useGroupedTypeImport", "useImportRestrictions", "useShorthandAssign", + "useShorthandFunctionType", ], diagnostics, ) @@ -2546,6 +2547,29 @@ impl VisitNode for Nursery { )); } }, + "useShorthandFunctionType" => match value { + AnyJsonValue::JsonStringValue(_) => { + let mut configuration = RuleConfiguration::default(); + self.map_to_known_string(&value, name_text, &mut configuration, diagnostics)?; + self.use_shorthand_function_type = Some(configuration); + } + AnyJsonValue::JsonObjectValue(_) => { + let mut rule_configuration = RuleConfiguration::default(); + rule_configuration.map_rule_configuration( + &value, + name_text, + "useShorthandFunctionType", + diagnostics, + )?; + self.use_shorthand_function_type = Some(rule_configuration); + } + _ => { + diagnostics.push(DeserializationDiagnostic::new_incorrect_type( + "object or string", + value.range(), + )); + } + }, _ => {} } Some(()) diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index ad3d7f6391af..35d357fa7bfa 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -1152,6 +1152,13 @@ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } ] + }, + "useShorthandFunctionType": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] } } }, diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 58bbc1065fc8..bdc66b09d5e7 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -806,6 +806,10 @@ export interface Nursery { * Require assignment operator shorthand where possible. */ useShorthandAssign?: RuleConfiguration; + /** + * Succinct description of the rule. + */ + useShorthandFunctionType?: RuleConfiguration; } /** * A list of rules that belong to this group @@ -1445,6 +1449,7 @@ export type Category = | "lint/nursery/useGroupedTypeImport" | "lint/nursery/useImportRestrictions" | "lint/nursery/useShorthandAssign" + | "lint/nursery/useShorthandFunctionType" | "lint/performance/noAccumulatingSpread" | "lint/performance/noDelete" | "lint/security/noDangerouslySetInnerHtml" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index ad3d7f6391af..35d357fa7bfa 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1152,6 +1152,13 @@ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } ] + }, + "useShorthandFunctionType": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] } } }, diff --git a/website/src/components/generated/NumberOfRules.astro b/website/src/components/generated/NumberOfRules.astro index 6b9300bc0694..ddca54d93573 100644 --- a/website/src/components/generated/NumberOfRules.astro +++ b/website/src/components/generated/NumberOfRules.astro @@ -1,2 +1,2 @@ -

Biome's linter has a total of 170 rules

\ No newline at end of file +

Biome's linter has a total of 171 rules

\ No newline at end of file diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index ab5a1482ab17..f34736b6472a 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -232,3 +232,4 @@ Rules that belong to this group are not subject to semantic versionimport type when an import only has specifiers with type qualifier. | ⚠️ | | [useImportRestrictions](/linter/rules/use-import-restrictions) | Disallows package private imports. | | | [useShorthandAssign](/linter/rules/use-shorthand-assign) | Require assignment operator shorthand where possible. | ⚠️ | +| [useShorthandFunctionType](/linter/rules/use-shorthand-function-type) | Succinct description of the rule. | | diff --git a/website/src/content/docs/linter/rules/use-shorthand-function-type.md b/website/src/content/docs/linter/rules/use-shorthand-function-type.md new file mode 100644 index 000000000000..16144ff380b2 --- /dev/null +++ b/website/src/content/docs/linter/rules/use-shorthand-function-type.md @@ -0,0 +1,54 @@ +--- +title: useShorthandFunctionType (since v1.3.0) +--- + +**Diagnostic Category: `lint/nursery/useShorthandFunctionType`** + +:::caution +This rule is part of the [nursery](/linter/rules/#nursery) group. +::: + +Succinct description of the rule. + +Put context and details about the rule. +As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + +Try to stay consistent with the descriptions of implemented rules. + +Add a link to the corresponding ESLint rule (if any): + +Source: https://eslint.org/docs/latest/rules/rule-name + +## Examples + +### Invalid + +```jsx +var a = 1; +a = 2; +``` + +

nursery/useShorthandFunctionType.js:1:11 lint/nursery/useShorthandFunctionType ━━━━━━━━━━━━━━━━━━━━━
+
+   Variable is read here.
+  
+  > 1 │ var a = 1;
+             
+  > 2 │ a = 2;
+   ^^
+    3 │ 
+  
+   This note will give you more information.
+  
+
+ +## Valid + +```jsx +var a = 1; +``` + +## Related links + +- [Disable a rule](/linter/#disable-a-lint-rule) +- [Rule options](/linter/#rule-options)