feat(linter): typescript-eslint/prefer-enum-initializers (#3097)

This commit is contained in:
Todor Andonov 2024-04-26 16:09:05 +03:00 committed by GitHub
parent 51de41cc7a
commit 388ee5135a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 160 additions and 0 deletions

View file

@ -138,6 +138,7 @@ mod typescript {
pub mod no_unsafe_declaration_merging;
pub mod no_var_requires;
pub mod prefer_as_const;
pub mod prefer_enum_initializers;
pub mod prefer_for_of;
pub mod prefer_function_type;
pub mod prefer_ts_expect_error;
@ -471,6 +472,7 @@ oxc_macros::declare_all_lint_rules! {
typescript::array_type,
typescript::ban_ts_comment,
typescript::ban_tslint_comment,
typescript::prefer_enum_initializers,
typescript::ban_types,
typescript::consistent_type_definitions,
typescript::no_duplicate_enum_values,

View file

@ -0,0 +1,110 @@
use oxc_ast::{ast::TSEnumMemberName, AstKind};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error("typescript-eslint(prefer-enum-initializers):The value of the member {0:?} should be explicitly defined.")]
#[diagnostic(severity(warning), help("Can be fixed to {0:?} = {1:?}."))]
struct PreferEnumInitializersDiagnostic(CompactStr, usize, #[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct PreferEnumInitializers;
declare_oxc_lint!(
/// ### What it does
/// Require each enum member value to be explicitly initialized.
///
/// ### Why is this bad?
/// In projects where the value of `enum` members are important, allowing implicit values for enums can cause bugs if enums are modified over time.
///
/// ### Example
/// ```typescript
/// // wrong, the value of `Close` is not constant
/// enum Status {
/// Open = 1,
/// Close,
/// }
/// ```
PreferEnumInitializers,
pedantic
);
impl Rule for PreferEnumInitializers {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::TSEnumDeclaration(decl) = node.kind() else { return };
for (index, member) in decl.members.iter().enumerate() {
if member.initializer.is_none() {
if let TSEnumMemberName::Identifier(i) = &member.id {
ctx.diagnostic(PreferEnumInitializersDiagnostic(
i.name.to_compact_str(),
index + 1,
member.span,
));
}
}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"
enum Direction {}
",
"
enum Direction {
Up = 1,
}
",
"
enum Direction {
Up = 1,
Down = 2,
}
",
"
enum Direction {
Up = 'Up',
Down = 'Down',
}
",
];
let fail = vec![
"
enum Direction {
Up,
}
",
"
enum Direction {
Up,
Down,
}
",
"
enum Direction {
Up = 'Up',
Down,
}
",
"
enum Direction {
Up,
Down = 'Down',
}
",
];
Tester::new(PreferEnumInitializers::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,48 @@
---
source: crates/oxc_linter/src/tester.rs
expression: prefer_enum_initializers
---
⚠ typescript-eslint(prefer-enum-initializers):The value of the member "Up" should be explicitly defined.
╭─[prefer_enum_initializers.tsx:3:6]
2 │ enum Direction {
3 │ Up,
· ──
4 │ }
╰────
help: Can be fixed to "Up" = 1.
⚠ typescript-eslint(prefer-enum-initializers):The value of the member "Up" should be explicitly defined.
╭─[prefer_enum_initializers.tsx:3:6]
2 │ enum Direction {
3 │ Up,
· ──
4 │ Down,
╰────
help: Can be fixed to "Up" = 1.
⚠ typescript-eslint(prefer-enum-initializers):The value of the member "Down" should be explicitly defined.
╭─[prefer_enum_initializers.tsx:4:6]
3 │ Up,
4 │ Down,
· ────
5 │ }
╰────
help: Can be fixed to "Down" = 2.
⚠ typescript-eslint(prefer-enum-initializers):The value of the member "Down" should be explicitly defined.
╭─[prefer_enum_initializers.tsx:4:6]
3 │ Up = 'Up',
4 │ Down,
· ────
5 │ }
╰────
help: Can be fixed to "Down" = 2.
⚠ typescript-eslint(prefer-enum-initializers):The value of the member "Up" should be explicitly defined.
╭─[prefer_enum_initializers.tsx:3:6]
2 │ enum Direction {
3 │ Up,
· ──
4 │ Down = 'Down',
╰────
help: Can be fixed to "Up" = 1.