From 60b28fc23a09c28e3da09838640943a284b88487 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:21:08 +0000 Subject: [PATCH] feat(linter): implement typescript/consistent-generic-constructors (#7497) --- crates/oxc_linter/src/rules.rs | 2 + .../consistent_generic_constructors.rs | 689 ++++++++++++++++++ ...cript_consistent_generic_constructors.snap | 252 +++++++ 3 files changed, 943 insertions(+) create mode 100644 crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs create mode 100644 crates/oxc_linter/src/snapshots/typescript_consistent_generic_constructors.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index aa9864ebd5a96..6023bfe44d9f9 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -155,6 +155,7 @@ mod typescript { pub mod ban_ts_comment; pub mod ban_tslint_comment; pub mod ban_types; + pub mod consistent_generic_constructors; pub mod consistent_indexed_object_style; pub mod consistent_type_definitions; pub mod consistent_type_imports; @@ -848,6 +849,7 @@ oxc_macros::declare_all_lint_rules! { typescript::ban_ts_comment, typescript::ban_tslint_comment, typescript::ban_types, + typescript::consistent_generic_constructors, typescript::consistent_indexed_object_style, typescript::consistent_type_definitions, typescript::consistent_type_imports, diff --git a/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs b/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs new file mode 100644 index 0000000000000..7b172dc5c2c5f --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/consistent_generic_constructors.rs @@ -0,0 +1,689 @@ +use oxc_ast::{ + ast::{Expression, TSType, TSTypeAnnotation, TSTypeName}, + AstKind, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +fn consistent_generic_constructors_diagnostic_prefer_annotation(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn( + "The generic type arguments should be specified as part of the type annotation.", + ) + .with_help("Move the generic type to the type annotation") + .with_label(span) +} +fn consistent_generic_constructors_diagnostic_prefer_constructor(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn( + "The generic type arguments should be specified as part of the constructor type arguments.", + ) + .with_help("Move the type annotation to the constructor") + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct ConsistentGenericConstructors(Box); + +#[derive(Debug, Default, Clone)] +pub struct ConsistentGenericConstructorsConfig { + option: PreferGenericType, +} + +#[derive(Debug, Default, Clone)] +enum PreferGenericType { + #[default] + Constructor, + TypeAnnotation, +} + +impl TryFrom<&str> for PreferGenericType { + type Error = &'static str; + + fn try_from(value: &str) -> Result { + match value { + "constructor" => Ok(Self::Constructor), + "type-annotation" => Ok(Self::TypeAnnotation), + _ => Err("Invalid value"), + } + } +} + +declare_oxc_lint!( + /// ### What it does + /// + /// When constructing a generic class, you can specify the type arguments on either the left-hand side (as a type annotation) or the right-hand side (as part of the constructor call). + /// + /// This rule enforces consistency in the way generic constructors are used. + /// + /// ### Why is this bad? + /// + /// Inconsistent usage of generic constructors can make the code harder to read and maintain. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```ts + /// const a: Foo = new Foo(); + /// const a = new Foo(); // prefer type annotation + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```ts + /// const a = new Foo(); + /// const a: Foo = new Foo(); // prefer type annotation + /// ``` + ConsistentGenericConstructors, + style, + pending +); + +impl Rule for ConsistentGenericConstructors { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::VariableDeclarator(variable_declarator) => { + let type_ann = &variable_declarator.id.type_annotation; + let init = &variable_declarator.init; + self.check(type_ann, init.as_ref(), ctx); + } + AstKind::AssignmentPattern(assignment_pattern) => { + let Some(parent) = ctx.nodes().parent_kind(node.id()) else { + return; + }; + + if !matches!(parent, AstKind::FormalParameter(_)) { + return; + } + + let type_ann = &assignment_pattern.left.type_annotation; + let init = &assignment_pattern.right; + self.check(type_ann, Some(init), ctx); + } + AstKind::PropertyDefinition(property_definition) => { + let type_ann = &property_definition.type_annotation; + let init = &property_definition.value; + + self.check(type_ann, init.as_ref(), ctx); + } + _ => {} + } + } + + fn from_configuration(value: serde_json::Value) -> Self { + Self(Box::new(ConsistentGenericConstructorsConfig { + option: value + .get(0) + .and_then(|v| v.as_str()) + .and_then(|s| PreferGenericType::try_from(s).ok()) + .unwrap_or_default(), + })) + } +} + +impl ConsistentGenericConstructors { + fn check( + &self, + type_annotation: &Option>, + init: Option<&Expression>, + ctx: &LintContext, + ) { + let Some(init) = init else { return }; + let Expression::NewExpression(new_expression) = init.get_inner_expression() else { + return; + }; + let Expression::Identifier(identifier) = &new_expression.callee else { + return; + }; + if let Some(type_annotation) = type_annotation { + if let TSType::TSTypeReference(type_annotation) = &type_annotation.type_annotation { + if let TSTypeName::IdentifierReference(ident) = &type_annotation.type_name { + if ident.name != identifier.name { + return; + } + } else { + return; + } + } else { + return; + } + } + + if matches!(self.0.option, PreferGenericType::TypeAnnotation) { + if type_annotation.is_none() { + if let Some(type_arguments) = &new_expression.type_parameters { + ctx.diagnostic(consistent_generic_constructors_diagnostic_prefer_annotation( + type_arguments.span, + )); + } + } + return; + } + + if let Some(type_arguments) = &type_annotation { + if has_type_parameters(&type_arguments.type_annotation) + && new_expression.type_parameters.is_none() + { + ctx.diagnostic(consistent_generic_constructors_diagnostic_prefer_constructor( + type_arguments.span, + )); + } + } + } +} + +fn has_type_parameters(ts_type: &TSType) -> bool { + match ts_type { + TSType::TSTypeReference(type_ref) => type_ref.type_parameters.is_some(), + _ => false, + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + ("const a = new Foo();", None), + ("const a = new Foo();", None), + ("const a: Foo = new Foo();", None), + ("const a: Foo = new Foo();", None), + ("const a: Bar = new Foo();", None), + ("const a: Foo = new Foo();", None), + ("const a: Bar = new Foo();", None), + ("const a: Bar = new Foo();", None), + ("const a: Foo = Foo();", None), + ("const a: Foo = Foo();", None), + ("const a: Foo = Foo();", None), + ( + " + class Foo { + a = new Foo(); + } + ", + None, + ), + ( + " + function foo(a: Foo = new Foo()) {} + ", + None, + ), + ( + " + function foo({ a }: Foo = new Foo()) {} + ", + None, + ), + ( + " + function foo([a]: Foo = new Foo()) {} + ", + None, + ), + ( + " + class A { + constructor(a: Foo = new Foo()) {} + } + ", + None, + ), + ( + " + const a = function (a: Foo = new Foo()) {}; + ", + None, + ), + ("const a = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Bar = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Bar = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a: Foo = Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a = new (class C {})();", Some(serde_json::json!(["type-annotation"]))), + ( + " + class Foo { + a: Foo = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo(a: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo({ a }: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo([a]: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class A { + constructor(a: Foo = new Foo()) {} + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + const a = function (a: Foo = new Foo()) {}; + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + const [a = new Foo()] = []; + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function a([a = new Foo()]) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ]; + + let fail = vec![ + ("const a: Foo = new Foo();", None), + ("const a: Map = new Map();", None), + ("const a: Map = new Map();", None), + ("const a: Map< string, number > = new Map();", None), + ("const a: Map = new Map ();", None), + ("const a: Foo = new Foo;", None), + ("const a: /* comment */ Foo/* another */ = new Foo();", None), + ("const a: Foo/* comment */ = new Foo /* another */();", None), + ( + "const a: Foo = new + Foo + ();", + None, + ), + ( + " + class Foo { + a: Foo = new Foo(); + } + ", + None, + ), + ( + " + class Foo { + [a]: Foo = new Foo(); + } + ", + None, + ), + ( + " + function foo(a: Foo = new Foo()) {} + ", + None, + ), + ( + " + function foo({ a }: Foo = new Foo()) {} + ", + None, + ), + ( + " + function foo([a]: Foo = new Foo()) {} + ", + None, + ), + ( + " + class A { + constructor(a: Foo = new Foo()) {} + } + ", + None, + ), + ( + " + const a = function (a: Foo = new Foo()) {}; + ", + None, + ), + ("const a = new Foo();", Some(serde_json::json!(["type-annotation"]))), + ("const a = new Map();", Some(serde_json::json!(["type-annotation"]))), + ("const a = new Map ();", Some(serde_json::json!(["type-annotation"]))), + ("const a = new Map< string, number >();", Some(serde_json::json!(["type-annotation"]))), + ( + "const a = new + Foo + ();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Foo/* comment */ /* another */();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Foo();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + a = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + [a] = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + [a + b] = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo(a = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo({ a } = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo([a] = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class A { + constructor(a = new Foo()) {} + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + const a = function (a = new Foo()) {}; + ", + Some(serde_json::json!(["type-annotation"])), + ), + ]; + + let _fix = vec![ + ("const a: Foo = new Foo();", "const a = new Foo();", None), + ("const a: Map = new Map();", "const a = new Map();", None), + ( + "const a: Map = new Map();", + "const a = new Map();", + None, + ), + ( + "const a: Map< string, number > = new Map();", + "const a = new Map< string, number >();", + None, + ), + ( + "const a: Map = new Map ();", + "const a = new Map ();", + None, + ), + ("const a: Foo = new Foo;", "const a = new Foo();", None), + ( + "const a: /* comment */ Foo/* another */ = new Foo();", + "const a = new Foo/* comment *//* another */();", + None, + ), + ( + "const a: Foo/* comment */ = new Foo /* another */();", + "const a = new Foo/* comment */ /* another */();", + None, + ), + ( + "const a: Foo = new + Foo + ();", + "const a = new + Foo + ();", + None, + ), + ( + " + class Foo { + a: Foo = new Foo(); + } + ", + " + class Foo { + a = new Foo(); + } + ", + None, + ), + ( + " + class Foo { + [a]: Foo = new Foo(); + } + ", + " + class Foo { + [a] = new Foo(); + } + ", + None, + ), + ( + " + function foo(a: Foo = new Foo()) {} + ", + " + function foo(a = new Foo()) {} + ", + None, + ), + ( + " + function foo({ a }: Foo = new Foo()) {} + ", + " + function foo({ a } = new Foo()) {} + ", + None, + ), + ( + " + function foo([a]: Foo = new Foo()) {} + ", + " + function foo([a] = new Foo()) {} + ", + None, + ), + ( + " + class A { + constructor(a: Foo = new Foo()) {} + } + ", + " + class A { + constructor(a = new Foo()) {} + } + ", + None, + ), + ( + " + const a = function (a: Foo = new Foo()) {}; + ", + " + const a = function (a = new Foo()) {}; + ", + None, + ), + ( + "const a = new Foo();", + "const a: Foo = new Foo();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Map();", + "const a: Map = new Map();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Map ();", + "const a: Map = new Map ();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Map< string, number >();", + "const a: Map< string, number > = new Map();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new + Foo + ();", + "const a: Foo = new + Foo + ();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Foo/* comment */ /* another */();", + "const a: Foo = new Foo/* comment */ /* another */();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + "const a = new Foo();", + "const a: Foo = new Foo();", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + a = new Foo(); + } + ", + " + class Foo { + a: Foo = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + [a] = new Foo(); + } + ", + " + class Foo { + [a]: Foo = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class Foo { + [a + b] = new Foo(); + } + ", + " + class Foo { + [a + b]: Foo = new Foo(); + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo(a = new Foo()) {} + ", + " + function foo(a: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo({ a } = new Foo()) {} + ", + " + function foo({ a }: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + function foo([a] = new Foo()) {} + ", + " + function foo([a]: Foo = new Foo()) {} + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + class A { + constructor(a = new Foo()) {} + } + ", + " + class A { + constructor(a: Foo = new Foo()) {} + } + ", + Some(serde_json::json!(["type-annotation"])), + ), + ( + " + const a = function (a = new Foo()) {}; + ", + " + const a = function (a: Foo = new Foo()) {}; + ", + Some(serde_json::json!(["type-annotation"])), + ), + ]; + Tester::new( + ConsistentGenericConstructors::NAME, + ConsistentGenericConstructors::CATEGORY, + pass, + fail, + ) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/typescript_consistent_generic_constructors.snap b/crates/oxc_linter/src/snapshots/typescript_consistent_generic_constructors.snap new file mode 100644 index 0000000000000..d32dad0ce21c3 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/typescript_consistent_generic_constructors.snap @@ -0,0 +1,252 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Foo = new Foo(); + · ───────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Map = new Map(); + · ───────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Map = new Map(); + · ────────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Map< string, number > = new Map(); + · ─────────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Map = new Map (); + · ───────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Foo = new Foo; + · ───────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: /* comment */ Foo/* another */ = new Foo(); + · ───────────────────────────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Foo/* comment */ = new Foo /* another */(); + · ─────────────────────────── + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:1:8] + 1 │ const a: Foo = new + · ───────────── + 2 │ Foo + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:3:7] + 2 │ class Foo { + 3 │ a: Foo = new Foo(); + · ───────────── + 4 │ } + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:3:9] + 2 │ class Foo { + 3 │ [a]: Foo = new Foo(); + · ───────────── + 4 │ } + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:2:18] + 1 │ + 2 │ function foo(a: Foo = new Foo()) {} + · ───────────── + 3 │ + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:2:22] + 1 │ + 2 │ function foo({ a }: Foo = new Foo()) {} + · ───────────── + 3 │ + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:2:20] + 1 │ + 2 │ function foo([a]: Foo = new Foo()) {} + · ───────────── + 3 │ + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:3:19] + 2 │ class A { + 3 │ constructor(a: Foo = new Foo()) {} + · ───────────── + 4 │ } + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the constructor type arguments. + ╭─[consistent_generic_constructors.tsx:2:25] + 1 │ + 2 │ const a = function (a: Foo = new Foo()) {}; + · ───────────── + 3 │ + ╰──── + help: Move the type annotation to the constructor + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:18] + 1 │ const a = new Foo(); + · ──────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:18] + 1 │ const a = new Map(); + · ──────────────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:19] + 1 │ const a = new Map (); + · ──────────────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:18] + 1 │ const a = new Map< string, number >(); + · ────────────────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:2:8] + 1 │ const a = new + 2 │ Foo + · ──────── + 3 │ (); + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:32] + 1 │ const a = new Foo/* comment */ /* another */(); + · ──────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:1:18] + 1 │ const a = new Foo(); + · ──────────────────────────────────────────── + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:3:17] + 2 │ class Foo { + 3 │ a = new Foo(); + · ──────── + 4 │ } + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:3:19] + 2 │ class Foo { + 3 │ [a] = new Foo(); + · ──────── + 4 │ } + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:3:23] + 2 │ class Foo { + 3 │ [a + b] = new Foo(); + · ──────── + 4 │ } + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:2:28] + 1 │ + 2 │ function foo(a = new Foo()) {} + · ──────── + 3 │ + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:2:32] + 1 │ + 2 │ function foo({ a } = new Foo()) {} + · ──────── + 3 │ + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:2:30] + 1 │ + 2 │ function foo([a] = new Foo()) {} + · ──────── + 3 │ + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:3:29] + 2 │ class A { + 3 │ constructor(a = new Foo()) {} + · ──────── + 4 │ } + ╰──── + help: Move the generic type to the type annotation + + ⚠ typescript-eslint(consistent-generic-constructors): The generic type arguments should be specified as part of the type annotation. + ╭─[consistent_generic_constructors.tsx:2:35] + 1 │ + 2 │ const a = function (a = new Foo()) {}; + · ──────── + 3 │ + ╰──── + help: Move the generic type to the type annotation