diff --git a/crates/rome_diagnostics_categories/src/categories.rs b/crates/rome_diagnostics_categories/src/categories.rs index 95f01416c10..9d30220d40c 100644 --- a/crates/rome_diagnostics_categories/src/categories.rs +++ b/crates/rome_diagnostics_categories/src/categories.rs @@ -64,6 +64,7 @@ define_dategories! { "lint/nursery/noInvalidConstructorSuper": "https://docs.rome.tools/lint/rules/noInvalidConstructorSuper", "lint/nursery/noConfusingLabels": "https://docs.rome.tools/lint/rules/noConfusingLabels", "lint/nursery/noNonNullAssertion": "https://docs.rome.tools/lint/rules/noNonNullAssertion", + "lint/nursery/noParameterProperties": "https://docs.rome.tools/lint/rules/noParameterProperties", "lint/nursery/noPrecisionLoss": "https://docs.rome.tools/lint/rules/noPrecisionLoss", "lint/nursery/noRedundantAlt": "https://docs.rome.tools/lint/rules/noRedundantAlt", "lint/nursery/noRedundantUseStrict": "https://docs.rome.tools/lint/rules/noRedundantUseStrict", diff --git a/crates/rome_js_analyze/src/analyzers/nursery.rs b/crates/rome_js_analyze/src/analyzers/nursery.rs index bf21aed3974..0fc32064226 100644 --- a/crates/rome_js_analyze/src/analyzers/nursery.rs +++ b/crates/rome_js_analyze/src/analyzers/nursery.rs @@ -23,6 +23,7 @@ mod no_inferrable_types; mod no_inner_declarations; mod no_invalid_constructor_super; mod no_non_null_assertion; +mod no_parameter_properties; mod no_precision_loss; mod no_prototype_builtins; mod no_redundant_alt; @@ -46,4 +47,4 @@ mod use_is_nan; mod use_media_caption; mod use_numeric_literals; mod use_yield; -declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_assign_in_expressions :: NoAssignInExpressions , self :: no_banned_types :: NoBannedTypes , self :: no_comma_operator :: NoCommaOperator , self :: no_confusing_labels :: NoConfusingLabels , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_duplicate_case :: NoDuplicateCase , self :: no_duplicate_class_members :: NoDuplicateClassMembers , self :: no_duplicate_jsx_props :: NoDuplicateJsxProps , self :: no_duplicate_object_keys :: NoDuplicateObjectKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_extra_labels :: NoExtraLabels , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_extra_semicolons :: NoExtraSemicolons , self :: no_global_object_calls :: NoGlobalObjectCalls , self :: no_header_scope :: NoHeaderScope , self :: no_inferrable_types :: NoInferrableTypes , self :: no_inner_declarations :: NoInnerDeclarations , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_precision_loss :: NoPrecisionLoss , self :: no_prototype_builtins :: NoPrototypeBuiltins , self :: no_redundant_alt :: NoRedundantAlt , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_self_compare :: NoSelfCompare , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_switch_declarations :: NoSwitchDeclarations , self :: no_unreachable_super :: NoUnreachableSuper , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_unused_labels :: NoUnusedLabels , self :: no_useless_rename :: NoUselessRename , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_void_type_return :: NoVoidTypeReturn , self :: no_with :: NoWith , self :: use_default_parameter_last :: UseDefaultParameterLast , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_enum_initializers :: UseEnumInitializers , self :: use_exponentiation_operator :: UseExponentiationOperator , self :: use_is_nan :: UseIsNan , self :: use_media_caption :: UseMediaCaption , self :: use_numeric_literals :: UseNumericLiterals , self :: use_yield :: UseYield ,] } } +declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_access_key :: NoAccessKey , self :: no_assign_in_expressions :: NoAssignInExpressions , self :: no_banned_types :: NoBannedTypes , self :: no_comma_operator :: NoCommaOperator , self :: no_confusing_labels :: NoConfusingLabels , self :: no_const_enum :: NoConstEnum , self :: no_constructor_return :: NoConstructorReturn , self :: no_distracting_elements :: NoDistractingElements , self :: no_duplicate_case :: NoDuplicateCase , self :: no_duplicate_class_members :: NoDuplicateClassMembers , self :: no_duplicate_jsx_props :: NoDuplicateJsxProps , self :: no_duplicate_object_keys :: NoDuplicateObjectKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_extra_labels :: NoExtraLabels , self :: no_extra_non_null_assertion :: NoExtraNonNullAssertion , self :: no_extra_semicolons :: NoExtraSemicolons , self :: no_global_object_calls :: NoGlobalObjectCalls , self :: no_header_scope :: NoHeaderScope , self :: no_inferrable_types :: NoInferrableTypes , self :: no_inner_declarations :: NoInnerDeclarations , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_non_null_assertion :: NoNonNullAssertion , self :: no_parameter_properties :: NoParameterProperties , self :: no_precision_loss :: NoPrecisionLoss , self :: no_prototype_builtins :: NoPrototypeBuiltins , self :: no_redundant_alt :: NoRedundantAlt , self :: no_redundant_use_strict :: NoRedundantUseStrict , self :: no_self_compare :: NoSelfCompare , self :: no_setter_return :: NoSetterReturn , self :: no_string_case_mismatch :: NoStringCaseMismatch , self :: no_switch_declarations :: NoSwitchDeclarations , self :: no_unreachable_super :: NoUnreachableSuper , self :: no_unsafe_finally :: NoUnsafeFinally , self :: no_unused_labels :: NoUnusedLabels , self :: no_useless_rename :: NoUselessRename , self :: no_useless_switch_case :: NoUselessSwitchCase , self :: no_void_type_return :: NoVoidTypeReturn , self :: no_with :: NoWith , self :: use_default_parameter_last :: UseDefaultParameterLast , self :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast , self :: use_enum_initializers :: UseEnumInitializers , self :: use_exponentiation_operator :: UseExponentiationOperator , self :: use_is_nan :: UseIsNan , self :: use_media_caption :: UseMediaCaption , self :: use_numeric_literals :: UseNumericLiterals , self :: use_yield :: UseYield ,] } } diff --git a/crates/rome_js_analyze/src/analyzers/nursery/no_parameter_properties.rs b/crates/rome_js_analyze/src/analyzers/nursery/no_parameter_properties.rs new file mode 100644 index 00000000000..5a37beaa6d3 --- /dev/null +++ b/crates/rome_js_analyze/src/analyzers/nursery/no_parameter_properties.rs @@ -0,0 +1,66 @@ +use rome_analyze::context::RuleContext; +use rome_analyze::{declare_rule, Ast, Rule, RuleDiagnostic}; +use rome_console::markup; +use rome_js_syntax::TsPropertyParameter; +use rome_rowan::AstNode; + +declare_rule! { + /// Disallow the use of parameter properties in class constructors. + /// + /// TypeScript includes a "parameter properties" shorthand for declaring a class constructor parameter and class property in one location. + /// Parameter properties can confuse those new to TypeScript as they are less explicit than other ways of declaring and initializing class members. + /// Moreover, private class properties, starting with `#`, cannot be turned into "parameter properties". + /// This questions the future of this feature. + /// + /// Source: https://typescript-eslint.io/rules/parameter-properties + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```ts,expect_diagnostic + /// class A { + /// constructor(readonly name: string) {} + /// } + /// ``` + /// + /// ### Valid + /// + /// ```ts + /// class A { + /// constructor(name: string) {} + /// } + /// ``` + /// + pub(crate) NoParameterProperties { + version: "next", + name: "noParameterProperties", + recommended: false, + } +} + +impl Rule for NoParameterProperties { + type Query = Ast; + type State = (); + type Signals = Option; + type Options = (); + + fn run(_: &RuleContext) -> Self::Signals { + Some(()) + } + + fn diagnostic(ctx: &RuleContext, _: &Self::State) -> Option { + let param_prop = ctx.query(); + Some(RuleDiagnostic::new( + rule_category!(), + param_prop.range(), + markup! { + "Use a more explicit ""class property"" instead of a ""parameter property""." + }, + ).note( + markup! { + "Parameter properties"" are less explicit than other ways of declaring and initializing ""class properties""." + } + )) + } +} diff --git a/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts new file mode 100644 index 00000000000..fa569afe78d --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts @@ -0,0 +1,27 @@ +export class Foo1 { + constructor(readonly name: string) {} +} + +export class Foo2 { + constructor(private name: string) {} +} + +export class Foo3 { + constructor(protected name: string) {} +} + +export class Foo4 { + constructor(public name: string) {} +} + +export class Foo5 { + constructor(private readonly name: string) {} +} + +export class Foo6 { + constructor(protected readonly name: string) {} +} + +export class Foo7 { + constructor(public readonly name: string) {} +} \ No newline at end of file diff --git a/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts.snap b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts.snap new file mode 100644 index 00000000000..d686874a8cb --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/invalid.ts.snap @@ -0,0 +1,149 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 92 +expression: invalid.ts +--- +# Input +```js +export class Foo1 { + constructor(readonly name: string) {} +} + +export class Foo2 { + constructor(private name: string) {} +} + +export class Foo3 { + constructor(protected name: string) {} +} + +export class Foo4 { + constructor(public name: string) {} +} + +export class Foo5 { + constructor(private readonly name: string) {} +} + +export class Foo6 { + constructor(protected readonly name: string) {} +} + +export class Foo7 { + constructor(public readonly name: string) {} +} +``` + +# Diagnostics +``` +invalid.ts:2:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 1 │ export class Foo1 { + > 2 │ constructor(readonly name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^^ + 3 │ } + 4 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:6:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 5 │ export class Foo2 { + > 6 │ constructor(private name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^ + 7 │ } + 8 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:10:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 9 │ export class Foo3 { + > 10 │ constructor(protected name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^^^ + 11 │ } + 12 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:14:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 13 │ export class Foo4 { + > 14 │ constructor(public name: string) {} + │ ^^^^^^^^^^^^^^^^^^^ + 15 │ } + 16 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:18:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 17 │ export class Foo5 { + > 18 │ constructor(private readonly name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 19 │ } + 20 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:22:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 21 │ export class Foo6 { + > 22 │ constructor(protected readonly name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 23 │ } + 24 │ + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + +``` +invalid.ts:26:15 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use a more explicit class property instead of a parameter property. + + 25 │ export class Foo7 { + > 26 │ constructor(public readonly name: string) {} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 27 │ } + + i Parameter properties are less explicit than other ways of declaring and initializing class properties. + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts new file mode 100644 index 00000000000..a437bba92a2 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts @@ -0,0 +1,3 @@ +export class Foo { + constructor(name: string) {} +} \ No newline at end of file diff --git a/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts.snap b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts.snap new file mode 100644 index 00000000000..29c76c55642 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/nursery/noParameterProperties/valid.ts.snap @@ -0,0 +1,13 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +assertion_line: 73 +expression: valid.ts +--- +# Input +```js +export class Foo { + constructor(name: string) {} +} +``` + + diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index a42dc5954fc..8b5ff769662 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -1005,6 +1005,9 @@ pub struct Nursery { #[doc = "Enforce that interactive ARIA roles are not assigned to non-interactive HTML elements."] #[serde(skip_serializing_if = "Option::is_none")] pub no_noninteractive_element_to_interactive_role: Option, + #[doc = "Disallow the use of parameter properties in class constructors."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_parameter_properties: Option, #[doc = "Disallow literal numbers that lose precision"] #[serde(skip_serializing_if = "Option::is_none")] pub no_precision_loss: Option, @@ -1110,7 +1113,7 @@ pub struct Nursery { } impl Nursery { const GROUP_NAME: &'static str = "nursery"; - pub(crate) const GROUP_RULES: [&'static str; 58] = [ + pub(crate) const GROUP_RULES: [&'static str; 59] = [ "noAccessKey", "noAssignInExpressions", "noBannedTypes", @@ -1135,6 +1138,7 @@ impl Nursery { "noInvalidConstructorSuper", "noNonNullAssertion", "noNoninteractiveElementToInteractiveRole", + "noParameterProperties", "noPrecisionLoss", "noPrototypeBuiltins", "noRedundantAlt", @@ -1243,8 +1247,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32]), @@ -1256,19 +1259,20 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -1393,176 +1397,181 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_precision_loss.as_ref() { + if let Some(rule) = self.no_parameter_properties.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_prototype_builtins.as_ref() { + if let Some(rule) = self.no_precision_loss.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_redundant_alt.as_ref() { + if let Some(rule) = self.no_prototype_builtins.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_redundant_use_strict.as_ref() { + if let Some(rule) = self.no_redundant_alt.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_restricted_globals.as_ref() { + if let Some(rule) = self.no_redundant_use_strict.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_self_compare.as_ref() { + if let Some(rule) = self.no_restricted_globals.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_setter_return.as_ref() { + if let Some(rule) = self.no_self_compare.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_string_case_mismatch.as_ref() { + if let Some(rule) = self.no_setter_return.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_switch_declarations.as_ref() { + if let Some(rule) = self.no_string_case_mismatch.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.no_unreachable_super.as_ref() { + if let Some(rule) = self.no_switch_declarations.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.no_unsafe_finally.as_ref() { + if let Some(rule) = self.no_unreachable_super.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.no_unused_labels.as_ref() { + if let Some(rule) = self.no_unsafe_finally.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.no_useless_rename.as_ref() { + if let Some(rule) = self.no_unused_labels.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.no_useless_switch_case.as_ref() { + if let Some(rule) = self.no_useless_rename.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.no_var.as_ref() { + if let Some(rule) = self.no_useless_switch_case.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.no_void_type_return.as_ref() { + if let Some(rule) = self.no_var.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.no_with.as_ref() { + if let Some(rule) = self.no_void_type_return.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_aria_prop_types.as_ref() { + if let Some(rule) = self.no_with.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_aria_props_for_role.as_ref() { + if let Some(rule) = self.use_aria_prop_types.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_camel_case.as_ref() { + if let Some(rule) = self.use_aria_props_for_role.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_const.as_ref() { + if let Some(rule) = self.use_camel_case.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_default_parameter_last.as_ref() { + if let Some(rule) = self.use_const.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_default_switch_clause_last.as_ref() { + if let Some(rule) = self.use_default_parameter_last.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_enum_initializers.as_ref() { + if let Some(rule) = self.use_default_switch_clause_last.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_exhaustive_dependencies.as_ref() { + if let Some(rule) = self.use_enum_initializers.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_exponentiation_operator.as_ref() { + if let Some(rule) = self.use_exhaustive_dependencies.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - if let Some(rule) = self.use_hook_at_top_level.as_ref() { + if let Some(rule) = self.use_exponentiation_operator.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_iframe_title.as_ref() { + if let Some(rule) = self.use_hook_at_top_level.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } } - if let Some(rule) = self.use_is_nan.as_ref() { + if let Some(rule) = self.use_iframe_title.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - if let Some(rule) = self.use_media_caption.as_ref() { + if let Some(rule) = self.use_is_nan.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); } } - if let Some(rule) = self.use_numeric_literals.as_ref() { + if let Some(rule) = self.use_media_caption.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - if let Some(rule) = self.use_valid_aria_props.as_ref() { + if let Some(rule) = self.use_numeric_literals.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } - if let Some(rule) = self.use_valid_lang.as_ref() { + if let Some(rule) = self.use_valid_aria_props.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); } } - if let Some(rule) = self.use_yield.as_ref() { + if let Some(rule) = self.use_valid_lang.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57])); } } + if let Some(rule) = self.use_yield.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -1687,176 +1696,181 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_precision_loss.as_ref() { + if let Some(rule) = self.no_parameter_properties.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_prototype_builtins.as_ref() { + if let Some(rule) = self.no_precision_loss.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_redundant_alt.as_ref() { + if let Some(rule) = self.no_prototype_builtins.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_redundant_use_strict.as_ref() { + if let Some(rule) = self.no_redundant_alt.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_restricted_globals.as_ref() { + if let Some(rule) = self.no_redundant_use_strict.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_self_compare.as_ref() { + if let Some(rule) = self.no_restricted_globals.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_setter_return.as_ref() { + if let Some(rule) = self.no_self_compare.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_string_case_mismatch.as_ref() { + if let Some(rule) = self.no_setter_return.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_switch_declarations.as_ref() { + if let Some(rule) = self.no_string_case_mismatch.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.no_unreachable_super.as_ref() { + if let Some(rule) = self.no_switch_declarations.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.no_unsafe_finally.as_ref() { + if let Some(rule) = self.no_unreachable_super.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.no_unused_labels.as_ref() { + if let Some(rule) = self.no_unsafe_finally.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.no_useless_rename.as_ref() { + if let Some(rule) = self.no_unused_labels.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.no_useless_switch_case.as_ref() { + if let Some(rule) = self.no_useless_rename.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.no_var.as_ref() { + if let Some(rule) = self.no_useless_switch_case.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.no_void_type_return.as_ref() { + if let Some(rule) = self.no_var.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.no_with.as_ref() { + if let Some(rule) = self.no_void_type_return.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_aria_prop_types.as_ref() { + if let Some(rule) = self.no_with.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_aria_props_for_role.as_ref() { + if let Some(rule) = self.use_aria_prop_types.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_camel_case.as_ref() { + if let Some(rule) = self.use_aria_props_for_role.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_const.as_ref() { + if let Some(rule) = self.use_camel_case.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_default_parameter_last.as_ref() { + if let Some(rule) = self.use_const.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_default_switch_clause_last.as_ref() { + if let Some(rule) = self.use_default_parameter_last.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_enum_initializers.as_ref() { + if let Some(rule) = self.use_default_switch_clause_last.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47])); } } - if let Some(rule) = self.use_exhaustive_dependencies.as_ref() { + if let Some(rule) = self.use_enum_initializers.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_exponentiation_operator.as_ref() { + if let Some(rule) = self.use_exhaustive_dependencies.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } - if let Some(rule) = self.use_hook_at_top_level.as_ref() { + if let Some(rule) = self.use_exponentiation_operator.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); } } - if let Some(rule) = self.use_iframe_title.as_ref() { + if let Some(rule) = self.use_hook_at_top_level.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[51])); } } - if let Some(rule) = self.use_is_nan.as_ref() { + if let Some(rule) = self.use_iframe_title.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[52])); } } - if let Some(rule) = self.use_media_caption.as_ref() { + if let Some(rule) = self.use_is_nan.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[53])); } } - if let Some(rule) = self.use_numeric_literals.as_ref() { + if let Some(rule) = self.use_media_caption.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[54])); } } - if let Some(rule) = self.use_valid_aria_props.as_ref() { + if let Some(rule) = self.use_numeric_literals.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[55])); } } - if let Some(rule) = self.use_valid_lang.as_ref() { + if let Some(rule) = self.use_valid_aria_props.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[56])); } } - if let Some(rule) = self.use_yield.as_ref() { + if let Some(rule) = self.use_valid_lang.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[57])); } } + if let Some(rule) = self.use_yield.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[58])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -1896,6 +1910,7 @@ impl Nursery { "noNoninteractiveElementToInteractiveRole" => { self.no_noninteractive_element_to_interactive_role.as_ref() } + "noParameterProperties" => self.no_parameter_properties.as_ref(), "noPrecisionLoss" => self.no_precision_loss.as_ref(), "noPrototypeBuiltins" => self.no_prototype_builtins.as_ref(), "noRedundantAlt" => self.no_redundant_alt.as_ref(), diff --git a/crates/rome_service/src/configuration/parse/json/rules.rs b/crates/rome_service/src/configuration/parse/json/rules.rs index 51ddc846d76..ae405aec710 100644 --- a/crates/rome_service/src/configuration/parse/json/rules.rs +++ b/crates/rome_service/src/configuration/parse/json/rules.rs @@ -727,6 +727,7 @@ impl VisitNode for Nursery { "noInvalidConstructorSuper", "noNonNullAssertion", "noNoninteractiveElementToInteractiveRole", + "noParameterProperties", "noPrecisionLoss", "noPrototypeBuiltins", "noRedundantAlt", @@ -1209,6 +1210,24 @@ impl VisitNode for Nursery { )); } }, + "noParameterProperties" => match value { + AnyJsonValue::JsonStringValue(_) => { + let mut configuration = RuleConfiguration::default(); + self.map_to_known_string(&value, name_text, &mut configuration, diagnostics)?; + self.no_parameter_properties = Some(configuration); + } + AnyJsonValue::JsonObjectValue(_) => { + let mut configuration = RuleConfiguration::default(); + self.map_to_object(&value, name_text, &mut configuration, diagnostics)?; + self.no_parameter_properties = Some(configuration); + } + _ => { + diagnostics.push(DeserializationDiagnostic::new_incorrect_type( + "object or string", + value.range(), + )); + } + }, "noPrecisionLoss" => match value { AnyJsonValue::JsonStringValue(_) => { let mut configuration = RuleConfiguration::default(); diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index 0162bf2df58..513c46e67fa 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -557,6 +557,13 @@ { "type": "null" } ] }, + "noParameterProperties": { + "description": "Disallow the use of parameter properties in class constructors.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noPrecisionLoss": { "description": "Disallow literal numbers that lose precision", "anyOf": [ diff --git a/npm/backend-jsonrpc/src/workspace.ts b/npm/backend-jsonrpc/src/workspace.ts index af355a1ff42..3ec8e462725 100644 --- a/npm/backend-jsonrpc/src/workspace.ts +++ b/npm/backend-jsonrpc/src/workspace.ts @@ -380,6 +380,10 @@ export interface Nursery { * Enforce that interactive ARIA roles are not assigned to non-interactive HTML elements. */ noNoninteractiveElementToInteractiveRole?: RuleConfiguration; + /** + * Disallow the use of parameter properties in class constructors. + */ + noParameterProperties?: RuleConfiguration; /** * Disallow literal numbers that lose precision */ @@ -811,6 +815,7 @@ export type Category = | "lint/nursery/noInvalidConstructorSuper" | "lint/nursery/noConfusingLabels" | "lint/nursery/noNonNullAssertion" + | "lint/nursery/noParameterProperties" | "lint/nursery/noPrecisionLoss" | "lint/nursery/noRedundantAlt" | "lint/nursery/noRedundantUseStrict" diff --git a/npm/rome/configuration_schema.json b/npm/rome/configuration_schema.json index 0162bf2df58..513c46e67fa 100644 --- a/npm/rome/configuration_schema.json +++ b/npm/rome/configuration_schema.json @@ -557,6 +557,13 @@ { "type": "null" } ] }, + "noParameterProperties": { + "description": "Disallow the use of parameter properties in class constructors.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noPrecisionLoss": { "description": "Disallow literal numbers that lose precision", "anyOf": [ diff --git a/website/src/pages/lint/rules/index.mdx b/website/src/pages/lint/rules/index.mdx index 75adeaef800..15028a805bb 100644 --- a/website/src/pages/lint/rules/index.mdx +++ b/website/src/pages/lint/rules/index.mdx @@ -630,6 +630,12 @@ Disallow non-null assertions using the ! postfix operator. Enforce that interactive ARIA roles are not assigned to non-interactive HTML elements.
+

+ noParameterProperties +

+Disallow the use of parameter properties in class constructors. +
+

noPrecisionLoss

diff --git a/website/src/pages/lint/rules/noParameterProperties.md b/website/src/pages/lint/rules/noParameterProperties.md new file mode 100644 index 00000000000..5612835eb78 --- /dev/null +++ b/website/src/pages/lint/rules/noParameterProperties.md @@ -0,0 +1,52 @@ +--- +title: Lint Rule noParameterProperties +parent: lint/rules/index +--- + +# noParameterProperties (since vnext) + +Disallow the use of parameter properties in class constructors. + +TypeScript includes a "parameter properties" shorthand for declaring a class constructor parameter and class property in one location. +Parameter properties can confuse those new to TypeScript as they are less explicit than other ways of declaring and initializing class members. +Moreover, private class properties, starting with `#`, cannot be turned into "parameter properties". +This questions the future of this feature. + +Source: https://typescript-eslint.io/rules/parameter-properties + +## Examples + +### Invalid + +```ts +class A { + constructor(readonly name: string) {} +} +``` + +
nursery/noParameterProperties.js:2:17 lint/nursery/noParameterProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+   Use a more explicit class property instead of a parameter property.
+  
+    1 │ class A {
+  > 2 │     constructor(readonly name: string) {}
+                   ^^^^^^^^^^^^^^^^^^^^^
+    3 │ }
+    4 │ 
+  
+   Parameter properties are less explicit than other ways of declaring and initializing class properties.
+  
+
+ +### Valid + +```ts +class A { + constructor(name: string) {} +} +``` + +## Related links + +- [Disable a rule](/linter/#disable-a-lint-rule) +- [Rule options](/linter/#rule-options)