diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 65991be34e834..e472e916e5adb 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -150,6 +150,7 @@ mod typescript { pub mod no_extra_non_null_assertion; pub mod no_extraneous_class; pub mod no_import_type_side_effects; + pub mod no_magic_numbers; pub mod no_misused_new; pub mod no_namespace; pub mod no_non_null_asserted_nullish_coalescing; @@ -502,7 +503,6 @@ oxc_macros::declare_all_lint_rules! { eslint::no_caller, eslint::no_case_declarations, eslint::no_class_assign, - eslint::no_multi_str, eslint::no_label_var, eslint::require_await, eslint::no_compare_neg_zero, @@ -537,6 +537,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_irregular_whitespace, eslint::no_iterator, eslint::no_loss_of_precision, + eslint::no_multi_str, eslint::no_new, eslint::no_new_func, eslint::no_new_wrappers, @@ -597,6 +598,7 @@ oxc_macros::declare_all_lint_rules! { typescript::no_explicit_any, typescript::no_extra_non_null_assertion, typescript::no_import_type_side_effects, + typescript::no_magic_numbers, typescript::no_misused_new, typescript::no_namespace, typescript::no_non_null_asserted_optional_chain, diff --git a/crates/oxc_linter/src/rules/typescript/no_magic_numbers.rs b/crates/oxc_linter/src/rules/typescript/no_magic_numbers.rs new file mode 100644 index 0000000000000..67ddc5bc322cd --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/no_magic_numbers.rs @@ -0,0 +1,930 @@ +use oxc_ast::{ + ast::{ + AssignmentTarget, Expression, MemberExpression, UnaryExpression, VariableDeclarationKind, + }, + AstKind, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_semantic::AstNodes; +use oxc_span::{GetSpan, Span}; +use oxc_syntax::operator::UnaryOperator; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +enum NoMagicNumberReportReason { + MustUseConst, + NoMagicNumber, +} + +fn must_use_const_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Number constants declarations must use 'const'.").with_label(span) +} + +fn no_magic_number_diagnostic(span: Span, raw: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("No magic number: {raw}")).with_label(span) +} + +#[derive(Debug, Default, Clone)] + +pub struct NoMagicNumbers(Box); + +impl std::ops::Deref for NoMagicNumbers { + type Target = NoMagicNumbersConfig; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NoMagicNumbersNumber { + Float(f64), + BigInt(String), +} + +#[derive(Debug, Default, Clone)] +pub struct NoMagicNumbersConfig { + ignore: Vec, + ignore_array_indexes: bool, + ignore_default_values: bool, + ignore_class_field_initial_values: bool, + enforce_const: bool, + detect_objects: bool, + ignore_enums: bool, + ignore_numeric_literal_types: bool, + ignore_readonly_class_properties: bool, + ignore_type_indexes: bool, +} + +impl TryFrom<&serde_json::Value> for NoMagicNumbersConfig { + type Error = OxcDiagnostic; + + fn try_from(raw: &serde_json::Value) -> Result { + if raw.is_null() { + return Ok(NoMagicNumbersConfig::default()); + } + + raw.get(0).map_or_else( + || { + Err(OxcDiagnostic::warn( + "Expecting object for typescript/no-magic-numbers configuration", + )) + }, + |object| { + fn get_bool_property(object: &serde_json::Value, index: &str) -> bool { + object.get(index).and_then(serde_json::Value::as_bool).unwrap_or_default() + } + Ok(Self { + ignore: object + .get("ignore") + .and_then(serde_json::Value::as_array) + .map(|v| { + v.iter() + .map(|v| { + if v.is_number() { + NoMagicNumbersNumber::Float(v.as_f64().unwrap()) + } else { + NoMagicNumbersNumber::BigInt(v.as_str().unwrap().to_owned()) + } + }) + .collect() + }) + .unwrap_or_default(), + ignore_array_indexes: get_bool_property(object, "ignoreArrayIndexes"), + ignore_default_values: get_bool_property(object, "ignoreDefaultValues"), + ignore_class_field_initial_values: get_bool_property( + object, + "ignoreClassFieldInitialValues", + ), + enforce_const: get_bool_property(object, "enforceConst"), + detect_objects: get_bool_property(object, "detectObjects"), + ignore_enums: get_bool_property(object, "ignoreEnums"), + ignore_numeric_literal_types: get_bool_property( + object, + "ignoreNumericLiteralTypes", + ), + ignore_readonly_class_properties: get_bool_property( + object, + "ignoreReadonlyClassProperties", + ), + ignore_type_indexes: get_bool_property(object, "ignoreTypeIndexes"), + }) + }, + ) + } +} + +declare_oxc_lint!( + /// ### What it does + /// + /// The no-magic-numbers rule aims to make code more readable and refactoring easier by ensuring that special numbers are declared as constants to make their meaning explicit. + /// The current implementation does not support BigInt numbers inside array indexes. + /// + /// ### Why is this bad? + /// + /// ‘Magic numbers’ are numbers that occur multiple times in code without an explicit meaning. They should preferably be replaced by named constants. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// + /// var dutyFreePrice = 100; + /// var finalPrice = dutyFreePrice + (dutyFreePrice * 0.25); + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignore": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "ignore": [1] }]*/ + /// var data = ['foo', 'bar', 'baz']; + /// var dataLast = data.length && data[data.length - 1]; + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreArrayIndexes": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "ignoreArrayIndexes": true }]*/ + /// var item = data[2]; + /// data[100] = a; + /// f(data[0]); + /// a = data[-0]; // same as data[0], -0 will be coerced to "0" + /// a = data[0xAB]; + /// a = data[5.6e1]; + /// a = data[4294967294]; // max array index + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreDefaultValues": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "ignoreDefaultValues": true }]*/ + /// const { tax = 0.25 } = accountancy; + /// function mapParallel(concurrency = 3) { /***/ } + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreClassFieldInitialValues": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "ignoreClassFieldInitialValues": true }]*/ + /// class C { + /// foo = 2; + /// bar = -3; + /// #baz = 4; + /// static qux = 5; + /// } + /// ``` + /// + /// Examples of **incorrect** code for this rule with option "enforceConst": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "enforceConst": true }]*/ + /// var TAX = 0.25; + /// ``` + /// + /// Examples of **incorrect** code for this rule with option "detectObjects": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "detectObjects": true }]*/ + /// var magic = { + /// tax: 0.25 + /// }; + /// ``` + /// + /// Examples of **correct** code for this rule with option "detectObjects": + /// ```javascript + /// /*typescript no-magic-numbers: ["error", { "detectObjects": true }]*/ + /// var TAX = 0.25; + /// + /// var magic = { + /// tax: TAX + /// }; + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreEnums": + /// ```typescript + /// /*typescript no-magic-numbers: ["error", { "ignoreEnums": true }]*/ + /// enum foo { + /// SECOND = 1000, + /// } + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreNumericLiteralTypes": + /// ```typescript + /// /*typescript no-magic-numbers: ["error", { "ignoreNumericLiteralTypes": true }]*/ + /// type SmallPrimes = 2 | 3 | 5 | 7 | 11; + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreReadonlyClassProperties": + /// ```typescript + /// /*typescript no-magic-numbers: ["error", { "ignoreReadonlyClassProperties": true }]*/ + /// class Foo { + /// readonly A = 1; + /// readonly B = 2; + /// public static readonly C = 1; + /// static readonly D = 1; + /// } + /// ``` + /// + /// Examples of **correct** code for this rule with option "ignoreTypeIndexes": + /// ```typescript + /// /*typescript no-magic-numbers: ["error", { "ignoreTypeIndexes": true }]*/ + /// type Foo = Bar[0]; + /// type Baz = Parameters[2]; + /// ``` + NoMagicNumbers, + style, + pending // TODO: enforceConst, probably copy from https://github.com/oxc-project/oxc/pull/5144 +); + +#[derive(Debug)] +struct InternConfig<'a> { + node: &'a AstNode<'a>, + value: NoMagicNumbersNumber, + raw: String, +} + +impl InternConfig<'_> { + pub fn from<'a>(node: &'a AstNode<'a>, parent_node: &'a AstNode<'a>) -> InternConfig<'a> { + let is_unary = matches!(parent_node.kind(), AstKind::UnaryExpression(_)); + let is_negative = matches!(parent_node.kind(), AstKind::UnaryExpression(unary) if unary.operator == UnaryOperator::UnaryNegation); + + match node.kind() { + AstKind::NumericLiteral(numeric) => { + if is_negative { + InternConfig { + node: parent_node, + value: NoMagicNumbersNumber::Float(0.0 - numeric.value), + raw: format!("-{}", numeric.raw), + } + } else { + InternConfig { + node: if is_unary { parent_node } else { node }, + value: NoMagicNumbersNumber::Float(numeric.value), + raw: numeric.raw.into(), + } + } + } + AstKind::BigIntLiteral(bigint) => { + let big_int_string = bigint.raw.clone().into_string(); + if is_negative { + let raw = format!("-{big_int_string}"); + + InternConfig { + node: parent_node, + value: NoMagicNumbersNumber::BigInt(raw.clone()), + raw: format!("-{raw}"), + } + } else { + InternConfig { + node: if is_unary { parent_node } else { node }, + value: NoMagicNumbersNumber::BigInt(big_int_string.clone()), + raw: big_int_string, + } + } + } + _ => { + unreachable!( + "expected AstKind BingIntLiteral or NumericLiteral, got {:?}", + node.kind().debug_name() + ) + } + } + } +} + +impl Rule for NoMagicNumbers { + fn from_configuration(value: serde_json::Value) -> Self { + Self(Box::new(NoMagicNumbersConfig::try_from(&value).unwrap())) + } + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if !matches!(node.kind(), AstKind::NumericLiteral(_) | AstKind::BigIntLiteral(_)) { + return; + } + + let nodes = ctx.semantic().nodes(); + let config = InternConfig::from(node, nodes.parent_node(node.id()).unwrap()); + + if self.is_skipable(&config, nodes) { + return; + } + + let parent_kind = nodes.parent_node(config.node.id()).unwrap().kind(); + let span = config.node.kind().span(); + + let Some(reason) = self.get_report_reason(&parent_kind) else { + return; + }; + + ctx.diagnostic(match reason { + NoMagicNumberReportReason::MustUseConst => must_use_const_diagnostic(span), + NoMagicNumberReportReason::NoMagicNumber => { + no_magic_number_diagnostic(span, &config.raw) + } + }); + } +} + +fn is_default_value(parent_kind: &AstKind<'_>) -> bool { + matches!(parent_kind, AstKind::AssignmentTargetWithDefault(_) | AstKind::AssignmentPattern(_)) +} + +fn is_class_field_initial_value(parent_kind: &AstKind<'_>) -> bool { + matches!(parent_kind, AstKind::PropertyDefinition(_)) +} + +fn is_detectable_object(parent_kind: &AstKind<'_>) -> bool { + matches!(parent_kind, AstKind::ObjectExpression(_) | AstKind::ObjectProperty(_)) +} + +fn is_parse_int_radix(parent_parent_node: &AstNode<'_>) -> bool { + let AstKind::CallExpression(expression) = parent_parent_node.kind() else { + return false; + }; + + let callee = expression.callee.without_parentheses(); + + callee.is_specific_id("parseInt") || callee.is_specific_member_access("Number", "parseInt") +} + +/// Returns whether the given node is used as an array index. +/// Value must coerce to a valid array index name: "0", "1", "2" ... "4294967294". +/// +/// All other values, like "-1", "2.5", or "4294967295", are just "normal" object properties, +/// which can be created and accessed on an array in addition to the array index properties, +/// but they don't affect array's length and are not considered by methods such as .map(), .forEach() etc. +/// +/// The maximum array length by the specification is 2 ** 32 - 1 = 4294967295, +/// thus the maximum valid index is 2 ** 32 - 2 = 4294967294. +/// +/// All notations are allowed, as long as the value coerces to one of "0", "1", "2" ... "4294967294". +/// +/// This current implementation does not check for BigInt Numbers, they will always accept them as array_index +/// Valid examples: +/// ```javascript +/// a[0], a[1], a[1.2e1], a[0xAB], a[0n], a[1n] +/// a[-0] // (same as a[0] because -0 coerces to "0") +/// a[-0n] // (-0n evaluates to 0n) +/// ``` +/// +/// Invalid examples: +/// ```javascript +/// a[-1], a[-0xAB], a[-1n], a[2.5], a[1.23e1], a[12e-1] +/// a[4294967295] // (above the max index, it's an access to a regular property a["4294967295"]) +/// a[999999999999999999999] // (even if it wasn't above the max index, it would be a["1e+21"]) +/// a[1e310] // (same as a["Infinity"]) +/// ``` +fn is_array_index<'a>(ast_kind: &AstKind<'a>, parent_kind: &AstKind<'a>) -> bool { + fn is_unanary_index(unary: &UnaryExpression) -> bool { + match &unary.argument { + Expression::BigIntLiteral(_) => true, + Expression::NumericLiteral(numeric) + if unary.operator == UnaryOperator::UnaryNegation => + { + numeric.value == 0.0 + } + _ => false, + } + } + + match ast_kind { + AstKind::UnaryExpression(unary) => is_unanary_index(unary), + AstKind::BigIntLiteral(_) => true, + AstKind::NumericLiteral(numeric) => match parent_kind { + AstKind::MemberExpression(expression) => { + if let MemberExpression::ComputedMemberExpression(computed_expression) = expression + { + return computed_expression.expression.is_number(numeric.value) + && numeric.value >= 0.0 + && numeric.value.fract() == 0.0 + && numeric.value < f64::from(u32::MAX); + } + + false + } + AstKind::UnaryExpression(unary) => is_unanary_index(unary), + _ => false, + }, + _ => false, + } +} + +fn is_ts_enum(parent_kind: &AstKind<'_>) -> bool { + matches!(parent_kind, AstKind::TSEnumMember(_)) +} + +fn is_ts_numeric_literal<'a>(parent_parent_node: &AstNode<'a>, nodes: &AstNodes<'a>) -> bool { + let mut node = parent_parent_node; + + while matches!( + node.kind(), + AstKind::TSUnionType(_) | AstKind::TSIntersectionType(_) | AstKind::TSParenthesizedType(_) + ) { + node = nodes.parent_node(node.id()).unwrap(); + } + + matches!(node.kind(), AstKind::TSTypeAliasDeclaration(_)) +} + +fn is_ts_readonly_property(parent_kind: &AstKind<'_>) -> bool { + matches!(parent_kind, AstKind::PropertyDefinition(property) if property.readonly) +} + +fn is_ts_indexed_access_type<'a>(parent_parent_node: &AstNode<'a>, nodes: &AstNodes<'a>) -> bool { + let mut node = parent_parent_node; + + while matches!( + node.kind(), + AstKind::TSUnionType(_) | AstKind::TSIntersectionType(_) | AstKind::TSParenthesizedType(_) + ) { + node = nodes.parent_node(node.id()).unwrap(); + } + + matches!(node.kind(), AstKind::TSIndexedAccessType(_)) +} + +impl NoMagicNumbers { + fn is_skipable<'a>(&self, config: &InternConfig<'a>, nodes: &AstNodes<'a>) -> bool { + if self.ignore.contains(&config.value) { + return true; + } + + let parent = nodes.parent_node(config.node.id()).unwrap(); + let parent_kind = parent.kind(); + + if self.ignore_enums && is_ts_enum(&parent_kind) { + return true; + } + + if self.ignore_readonly_class_properties && is_ts_readonly_property(&parent_kind) { + return true; + } + + if self.ignore_default_values && is_default_value(&parent_kind) { + return true; + } + + if self.ignore_class_field_initial_values && is_class_field_initial_value(&parent_kind) { + return true; + } + + if !self.detect_objects && is_detectable_object(&parent_kind) { + return true; + } + + let parent_parent = nodes.parent_node(parent.id()).unwrap(); + + if is_parse_int_radix(parent_parent) { + return true; + } + + if self.ignore_numeric_literal_types && is_ts_numeric_literal(parent_parent, nodes) { + return true; + } + + if self.ignore_type_indexes && is_ts_indexed_access_type(parent_parent, nodes) { + return true; + } + + // test it at the end because of false positives with BigInt values + if self.ignore_array_indexes && is_array_index(&config.node.kind(), &parent_kind) { + return true; + } + + false + } + + fn get_report_reason<'a>( + &self, + parent_kind: &'a AstKind<'a>, + ) -> Option { + match parent_kind { + AstKind::VariableDeclarator(declarator) => { + if self.enforce_const && declarator.kind != VariableDeclarationKind::Const { + return Some(NoMagicNumberReportReason::MustUseConst); + } + + None + } + AstKind::AssignmentExpression(expression) => { + if matches!(expression.left, AssignmentTarget::AssignmentTargetIdentifier(_)) { + return Some(NoMagicNumberReportReason::NoMagicNumber); + } + + None + } + AstKind::JSXExpressionContainer(_) => None, + _ => Some(NoMagicNumberReportReason::NoMagicNumber), + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let ignore_array_indexes = Some(serde_json::json!([{"ignoreArrayIndexes": true}])); + let ignore_default_values = Some(serde_json::json!([{"ignoreDefaultValues": true}])); + let enforce_const = Some(serde_json::json!([{ "enforceConst": true}])); + let ignore_class_field_initial_values = + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": true }])); + let ignore_numeric_literal_types = + Some(serde_json::json!([{ "ignoreNumericLiteralTypes": true }])); + let ignore_typed_index_arrays = Some(serde_json::json!([{ "ignoreTypeIndexes": true }])); + + let pass = vec![ + ("var x = parseInt(y, 10);", None), + ("var x = parseInt(y, -10);", None), + ("var x = Number.parseInt(y, 10);", None), + ("const foo = 42;", None), // { "ecmaVersion": 6 }, + ( + "var foo = 42;", + Some(serde_json::json!([{ "enforceConst": false }])), + ), // { "ecmaVersion": 6 }, + ("var foo = -42;", None), + ( + "var foo = 0 + 1 - 2 + -2;", + Some(serde_json::json!([{ "ignore": [0, 1, 2, -2] }])), + ), + ( + "var foo = 0 + 1 + 2 + 3 + 4;", + Some(serde_json::json!([{ "ignore": [0, 1, 2, 3, 4] }])), + ), + ("var foo = { bar:10 }", None), + ( + "setTimeout(function() {return 1;}, 0);", + Some(serde_json::json!([{ "ignore": [0, 1] }])), + ), + ("var data = ['foo', 'bar', 'baz']; var third = data[3];", ignore_array_indexes.clone()), + ("foo[0]", ignore_array_indexes.clone()), + ("foo[-0]", ignore_array_indexes.clone()), + ("foo[1]", ignore_array_indexes.clone()), + ("foo[100]", ignore_array_indexes.clone()), + ("foo[200.00]", ignore_array_indexes.clone()), + ("foo[3e4]", ignore_array_indexes.clone()), + ("foo[1.23e2]", ignore_array_indexes.clone()), + ("foo[230e-1]", ignore_array_indexes.clone()), + ("foo[0b110]", ignore_array_indexes.clone()), // { "ecmaVersion": 2015 }, + ("foo[0o71]", ignore_array_indexes.clone()), // { "ecmaVersion": 2015 }, + ("foo[0xABC]", ignore_array_indexes.clone()), + ("foo[0123]", ignore_array_indexes.clone()), // { "sourceType": "script" }, + ("foo[5.0000000000000001]", ignore_array_indexes.clone()), + ("foo[4294967294]", ignore_array_indexes.clone()), + ("foo[0n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[-0n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[1n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[100n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[0xABn]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[4294967294n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("var a = ;", None), // { "parserOptions": { "ecmaFeatures": { "jsx": true } } }, + ("var a =
;", None), // { "parserOptions": { "ecmaFeatures": { "jsx": true } } }, + ("f(100n)", Some(serde_json::json!([{ "ignore": ["100n"] }]))), // { "ecmaVersion": 2020 }, + ("f(-100n)", Some(serde_json::json!([{ "ignore": ["-100n"] }]))), // { "ecmaVersion": 2020 }, + ("const { param = 123 } = sourceObject;", ignore_default_values.clone()), // { "ecmaVersion": 6 }, + ("const func = (param = 123) => {}", ignore_default_values.clone()), // { "ecmaVersion": 6 }, + ("const func = ({ param = 123 }) => {}", ignore_default_values.clone()), // { "ecmaVersion": 6 }, + ("const [one = 1, two = 2] = []", ignore_default_values.clone()), // { "ecmaVersion": 6 }, + ("var one, two; [one = 1, two = 2] = []", ignore_default_values.clone()), // { "ecmaVersion": 6 }, + ("var x = parseInt?.(y, 10);", None), // { "ecmaVersion": 2020 }, + ("var x = Number?.parseInt(y, 10);", None), // { "ecmaVersion": 2020 }, + ("var x = (Number?.parseInt)(y, 10);", None), // { "ecmaVersion": 2020 }, + ("foo?.[777]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("class C { foo = 2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { foo = -2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { static foo = 2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { #foo = 2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { static #foo = 2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 } + ("const FOO = 10;", ignore_numeric_literal_types.clone()), + ("type Foo = 'bar';", None), + ("type Foo = true;", None), + ("type Foo = 1;", ignore_numeric_literal_types.clone()), + ("type Foo = -1;", ignore_numeric_literal_types.clone()), + ("type Foo = 1 | 2 | 3;", ignore_numeric_literal_types.clone()), + ("type Foo = 1 | -1;", ignore_numeric_literal_types.clone()), + ( + " + enum foo { + SECOND = 1000, + NUM = '0123456789', + NEG = -1, + POS = +1, + } + ", + Some(serde_json::json!([{ "ignoreEnums": true }])), + ), + ( + " + class Foo { + readonly A = 1; + readonly B = 2; + public static readonly C = 1; + static readonly D = 1; + readonly E = -1; + readonly F = +1; + private readonly G = 100n; + } + ", + Some(serde_json::json!([{ "ignoreReadonlyClassProperties": true }])), + ), + ("type Foo = Bar[0];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[-1];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[0xab];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[5.6e1];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[10n];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[1 | -2];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[1 & -2];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[1 & number];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar[((1 & -2) | 3) | 4];", ignore_typed_index_arrays.clone()), + ("type Foo = Parameters[2];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar['baz'];", ignore_typed_index_arrays.clone()), + ("type Foo = Bar['baz'];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ( + " + type Others = [['a'], ['b']]; + + type Foo = { + [K in keyof Others[0]]: Others[K]; + }; + ", + Some(serde_json::json!([{ "ignoreTypeIndexes": true }])), + ), + ("type Foo = 1;", Some(serde_json::json!([{ "ignore": [1] }]))), + ("type Foo = -2;", Some(serde_json::json!([{ "ignore": [-2] }]))), + ("type Foo = 3n;", Some(serde_json::json!([{ "ignore": ["3n"] }]))), + ("type Foo = -4n;", Some(serde_json::json!([{ "ignore": ["-4n"] }]))), + ("type Foo = 5.6;", Some(serde_json::json!([{ "ignore": [5.6] }]))), + ("type Foo = -7.8;", Some(serde_json::json!([{ "ignore": [-7.8] }]))), + ("type Foo = 0x0a;", Some(serde_json::json!([{ "ignore": [0x0a] }]))), + ("type Foo = -0xbc;", Some(serde_json::json!([{ "ignore": [-0xbc] }]))), + ("type Foo = 1e2;", Some(serde_json::json!([{ "ignore": [1e2] }]))), + ("type Foo = -3e4;", Some(serde_json::json!([{ "ignore": [-3e4] }]))), + ("type Foo = 5e-6;", Some(serde_json::json!([{ "ignore": [5e-6] }]))), + ("type Foo = -7e-8;", Some(serde_json::json!([{ "ignore": [-7e-8] }]))), + ("type Foo = 1.1e2;", Some(serde_json::json!([{ "ignore": [1.1e2] }]))), + ("type Foo = -3.1e4;", Some(serde_json::json!([{ "ignore": [-3.1e4] }]))), + ("type Foo = 5.1e-6;", Some(serde_json::json!([{ "ignore": [5.1e-6] }]))), + ("type Foo = -7.1e-8;", Some(serde_json::json!([{ "ignore": [-7.1e-8] }]))), + ( + " + interface Foo { + bar: 1; + } + ", + Some(serde_json::json!([{ "ignoreNumericLiteralTypes": true, "ignore": [1] }])), + ), + ( + " + enum foo { + SECOND = 1000, + NUM = '0123456789', + NEG = -1, + POS = +2, + } + ", + Some(serde_json::json!([{ "ignoreEnums": false, "ignore": [1000, -1, 2] }])), + ), + ( + " + class Foo { + readonly A = 1; + readonly B = 2; + public static readonly C = 3; + static readonly D = 4; + readonly E = -5; + readonly F = +6; + private readonly G = 100n; + private static readonly H = -2000n; + } + ", + Some( + serde_json::json!([ { "ignoreReadonlyClassProperties": false, "ignore": [1, 2, 3, 4, -5, 6, "100n", "-2000n"], }, ]), + ), + ), + ( + "type Foo = Bar[0];", + Some(serde_json::json!([{ "ignoreTypeIndexes": false, "ignore": [0] }])), + ), + ( + " + type Other = { + [0]: 3; + }; + + type Foo = { + [K in keyof Other]: `${K & number}`; + }; + ", + Some(serde_json::json!([{ "ignoreTypeIndexes": true, "ignore": [0, 3] }])), + ), + ]; + + let fail = vec![ + ("var foo = 42", enforce_const.clone()), // { "ecmaVersion": 6 }, + ("var foo = 0 + 1;", None), + ("var foo = 42n", enforce_const.clone()), // { "ecmaVersion": 2020 }, + ("var foo = 0n + 1n;", None), // { "ecmaVersion": 2020 }, + ("a = a + 5;", None), + ("a += 5;", None), + ("var foo = 0 + 1 + -2 + 2;", None), + ( + "var foo = 0 + 1 + 2;", + Some(serde_json::json!([{ "ignore": [0, 1] }])), + ), + ( + "var foo = { bar:10 }", + Some(serde_json::json!([{ "detectObjects": true }])), + ), + ("console.log(0x1A + 0x02); console.log(071);", None), // { "sourceType": "script" }, + ( + "var stats = {avg: 42};", + Some(serde_json::json!([{ "detectObjects": true }])), + ), + ("var colors = {}; colors.RED = 2; colors.YELLOW = 3; colors.BLUE = 4 + 5;", None), + ("function getSecondsInMinute() {return 60;}", None), + ("function getNegativeSecondsInMinute() {return -60;}", None), + ("var data = ['foo', 'bar', 'baz']; var third = data[3];", None), + ("var data = ['foo', 'bar', 'baz']; var third = data[3];", Some(serde_json::json!([{}]))), + ( + "var data = ['foo', 'bar', 'baz']; var third = data[3];", + Some(serde_json::json!([{"ignoreArrayIndexes": false}])), + ), + ("foo[-100]", ignore_array_indexes.clone()), + ("foo[-1.5]", ignore_array_indexes.clone()), + ("foo[-1]", ignore_array_indexes.clone()), + ("foo[-0.1]", ignore_array_indexes.clone()), + ("foo[-0b110]", ignore_array_indexes.clone()), // { "ecmaVersion": 2015 }, + ("foo[-0o71]", ignore_array_indexes.clone()), // { "ecmaVersion": 2015 }, + ("foo[-0x12]", ignore_array_indexes.clone()), + ("foo[-012]", ignore_array_indexes.clone()), // { "sourceType": "script" }, + ("foo[0.1]", ignore_array_indexes.clone()), + ("foo[0.12e1]", ignore_array_indexes.clone()), + ("foo[1.5]", ignore_array_indexes.clone()), + ("foo[1.678e2]", ignore_array_indexes.clone()), + ("foo[56e-1]", ignore_array_indexes.clone()), + ("foo[5.000000000000001]", ignore_array_indexes.clone()), + ("foo[100.9]", ignore_array_indexes.clone()), + ("foo[4294967295]", ignore_array_indexes.clone()), + ("foo[1e300]", ignore_array_indexes.clone()), + ("foo[1e310]", ignore_array_indexes.clone()), + ("foo[-1e310]", ignore_array_indexes.clone()), + // ("foo[-1n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + // ("foo[-100n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + // ("foo[-0x12n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + // ("foo[4294967295n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("foo[+0]", ignore_array_indexes.clone()), + ("foo[+1]", ignore_array_indexes.clone()), + ("foo[-(-1)]", ignore_array_indexes.clone()), + // ("foo[+0n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + // ("foo[+1n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + // ("foo[- -1n]", ignore_array_indexes.clone()), // { "ecmaVersion": 2020 }, + ("100 .toString()", ignore_array_indexes.clone()), + ("200[100]", ignore_array_indexes.clone()), + ("var a =
;", None), // { "parserOptions": { "ecmaFeatures": { "jsx": true } } }, + ("var min, max, mean; min = 1; max = 10; mean = 4;", Some(serde_json::json!([{}]))), + ("f(100n)", Some(serde_json::json!([{ "ignore": [100] }]))), // { "ecmaVersion": 2020 }, + ("f(-100n)", Some(serde_json::json!([{ "ignore": ["100n"] }]))), // { "ecmaVersion": 2020 }, + ("f(100n)", Some(serde_json::json!([{ "ignore": ["-100n"] }]))), // { "ecmaVersion": 2020 }, + ("f(100)", Some(serde_json::json!([{ "ignore": ["100n"] }]))), + ( + "const func = (param = 123) => {}", + Some(serde_json::json!([{ "ignoreDefaultValues": false }])), + ), // { "ecmaVersion": 6 }, + ("const { param = 123 } = sourceObject;", Some(serde_json::json!([{}]))), // { "ecmaVersion": 6 }, + ("const { param = 123 } = sourceObject;", None), // { "ecmaVersion": 6 }, + ( + "const { param = 123 } = sourceObject;", + Some(serde_json::json!([{ "ignoreDefaultValues": false }])), + ), // { "ecmaVersion": 6 }, + ( + "const [one = 1, two = 2] = []", + Some(serde_json::json!([{ "ignoreDefaultValues": false }])), + ), // { "ecmaVersion": 6 }, + ( + "var one, two; [one = 1, two = 2] = []", + Some(serde_json::json!([{ "ignoreDefaultValues": false }])), + ), // { "ecmaVersion": 6 }, + ("class C { foo = 2; }", None), // { "ecmaVersion": 2022 }, + ("class C { foo = 2; }", Some(serde_json::json!([{}]))), // { "ecmaVersion": 2022 }, + ( + "class C { foo = 2; }", + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": false }])), + ), // { "ecmaVersion": 2022 }, + ( + "class C { foo = -2; }", + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": false }])), + ), // { "ecmaVersion": 2022 }, + ( + "class C { static foo = 2; }", + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": false }])), + ), // { "ecmaVersion": 2022 }, + ( + "class C { #foo = 2; }", + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": false }])), + ), // { "ecmaVersion": 2022 }, + ( + "class C { static #foo = 2; }", + Some(serde_json::json!([{ "ignoreClassFieldInitialValues": false }])), + ), // { "ecmaVersion": 2022 }, + ("class C { foo = 2 + 3; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { 2; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 }, + ("class C { [2]; }", ignore_class_field_initial_values.clone()), // { "ecmaVersion": 2022 } + ("type Foo = 1;", Some(serde_json::json!([{ "ignoreNumericLiteralTypes": false }]))), + ("type Foo = -1;", Some(serde_json::json!([{ "ignoreNumericLiteralTypes": false }]))), + ( + "type Foo = 1 | 2 | 3;", + Some(serde_json::json!([{ "ignoreNumericLiteralTypes": false }])), + ), + ("type Foo = 1 | -1;", Some(serde_json::json!([{ "ignoreNumericLiteralTypes": false }]))), + ( + " + interface Foo { + bar: 1; + } + ", + Some(serde_json::json!([{ "ignoreNumericLiteralTypes": true }])), + ), + ( + " + enum foo { + SECOND = 1000, + NUM = '0123456789', + NEG = -1, + POS = +1, + } + ", + Some(serde_json::json!([{ "ignoreEnums": false }])), + ), + ( + " + class Foo { + readonly A = 1; + readonly B = 2; + public static readonly C = 3; + static readonly D = 4; + readonly E = -5; + readonly F = +6; + private readonly G = 100n; + } + ", + Some(serde_json::json!([{ "ignoreReadonlyClassProperties": false }])), + ), + ("type Foo = Bar[0];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[-1];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[0xab];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[5.6e1];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + // ("type Foo = Bar[10n];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[1 | -2];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[1 & -2];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ("type Foo = Bar[1 & number];", Some(serde_json::json!([{ "ignoreTypeIndexes": false }]))), + ( + "type Foo = Bar[((1 & -2) | 3) | 4];", + Some(serde_json::json!([{ "ignoreTypeIndexes": false }])), + ), + ( + "type Foo = Parameters[2];", + Some(serde_json::json!([{ "ignoreTypeIndexes": false }])), + ), + ( + " + type Others = [['a'], ['b']]; + + type Foo = { + [K in keyof Others[0]]: Others[K]; + }; + ", + Some(serde_json::json!([{ "ignoreTypeIndexes": false }])), + ), + ( + " + type Other = { + [0]: 3; + }; + + type Foo = { + [K in keyof Other]: `${K & number}`; + }; + ", + Some(serde_json::json!([{ "ignoreTypeIndexes": true }])), + ), + ( + " + type Foo = { + [K in 0 | 1 | 2]: 0; + }; + ", + Some(serde_json::json!([{ "ignoreTypeIndexes": true }])), + ), + ("type Foo = 1;", Some(serde_json::json!([{ "ignore": [-1] }]))), + ("type Foo = -2;", Some(serde_json::json!([{ "ignore": [2] }]))), + ("type Foo = 3n;", Some(serde_json::json!([{ "ignore": ["-3n"] }]))), + ("type Foo = -4n;", Some(serde_json::json!([{ "ignore": ["4n"] }]))), + ("type Foo = 5.6;", Some(serde_json::json!([{ "ignore": [-5.6] }]))), + ("type Foo = -7.8;", Some(serde_json::json!([{ "ignore": [7.8] }]))), + ("type Foo = 0x0a;", Some(serde_json::json!([{ "ignore": [-0x0a] }]))), + ("type Foo = -0xbc;", Some(serde_json::json!([{ "ignore": [0xbc] }]))), + ("type Foo = 1e2;", Some(serde_json::json!([{ "ignore": [-1e2] }]))), + ("type Foo = -3e4;", Some(serde_json::json!([{ "ignore": [3e4] }]))), + ("type Foo = 5e-6;", Some(serde_json::json!([{ "ignore": [-5e-6] }]))), + ("type Foo = -7e-8;", Some(serde_json::json!([{ "ignore": [7e-8] }]))), + ("type Foo = 1.1e2;", Some(serde_json::json!([{ "ignore": [-1.1e2] }]))), + ("type Foo = -3.1e4;", Some(serde_json::json!([{ "ignore": [3.1e4] }]))), + ("type Foo = 5.1e-6;", Some(serde_json::json!([{ "ignore": [-5.1e-6] }]))), + ("type Foo = -7.1e-8;", Some(serde_json::json!([{ "ignore": [7.1e-8] }]))), + ]; + + Tester::new(NoMagicNumbers::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_magic_numbers.snap b/crates/oxc_linter/src/snapshots/no_magic_numbers.snap new file mode 100644 index 0000000000000..7779c9f92f8d6 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_magic_numbers.snap @@ -0,0 +1,836 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ typescript-eslint(no-magic-numbers): Number constants declarations must use 'const'. + ╭─[no_magic_numbers.tsx:1:11] + 1 │ var foo = 42 + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:1:11] + 1 │ var foo = 0 + 1; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:15] + 1 │ var foo = 0 + 1; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): Number constants declarations must use 'const'. + ╭─[no_magic_numbers.tsx:1:11] + 1 │ var foo = 42n + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0n + ╭─[no_magic_numbers.tsx:1:11] + 1 │ var foo = 0n + 1n; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1n + ╭─[no_magic_numbers.tsx:1:16] + 1 │ var foo = 0n + 1n; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5 + ╭─[no_magic_numbers.tsx:1:9] + 1 │ a = a + 5; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5 + ╭─[no_magic_numbers.tsx:1:6] + 1 │ a += 5; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:1:11] + 1 │ var foo = 0 + 1 + -2 + 2; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:15] + 1 │ var foo = 0 + 1 + -2 + 2; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:19] + 1 │ var foo = 0 + 1 + -2 + 2; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:24] + 1 │ var foo = 0 + 1 + -2 + 2; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:19] + 1 │ var foo = 0 + 1 + 2; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 10 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ var foo = { bar:10 } + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0x1A + ╭─[no_magic_numbers.tsx:1:13] + 1 │ console.log(0x1A + 0x02); console.log(071); + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0x02 + ╭─[no_magic_numbers.tsx:1:20] + 1 │ console.log(0x1A + 0x02); console.log(071); + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 071 + ╭─[no_magic_numbers.tsx:1:39] + 1 │ console.log(0x1A + 0x02); console.log(071); + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 42 + ╭─[no_magic_numbers.tsx:1:19] + 1 │ var stats = {avg: 42}; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 4 + ╭─[no_magic_numbers.tsx:1:67] + 1 │ var colors = {}; colors.RED = 2; colors.YELLOW = 3; colors.BLUE = 4 + 5; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5 + ╭─[no_magic_numbers.tsx:1:71] + 1 │ var colors = {}; colors.RED = 2; colors.YELLOW = 3; colors.BLUE = 4 + 5; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 60 + ╭─[no_magic_numbers.tsx:1:39] + 1 │ function getSecondsInMinute() {return 60;} + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -60 + ╭─[no_magic_numbers.tsx:1:47] + 1 │ function getNegativeSecondsInMinute() {return -60;} + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:52] + 1 │ var data = ['foo', 'bar', 'baz']; var third = data[3]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:52] + 1 │ var data = ['foo', 'bar', 'baz']; var third = data[3]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:52] + 1 │ var data = ['foo', 'bar', 'baz']; var third = data[3]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -100 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-100] + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1.5 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-1.5] + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-1] + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -0.1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-0.1] + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -0b110 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-0b110] + · ────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -0o71 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-0o71] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -0x12 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-0x12] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -012 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-012] + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0.1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[0.1] + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0.12e1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[0.12e1] + · ────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1.5 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[1.5] + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1.678e2 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[1.678e2] + · ─────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 56e-1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[56e-1] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5.000000000000001 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[5.000000000000001] + · ───────────────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100.9 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[100.9] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 4294967295 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[4294967295] + · ────────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1e300 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[1e300] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1e310 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[1e310] + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1e310 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[-1e310] + · ────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[+0] + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:5] + 1 │ foo[+1] + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:1:7] + 1 │ foo[-(-1)] + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100 + ╭─[no_magic_numbers.tsx:1:1] + 1 │ 100 .toString() + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 200 + ╭─[no_magic_numbers.tsx:1:1] + 1 │ 200[100] + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:26] + 1 │ var a =
; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:28] + 1 │ var a =
; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:30] + 1 │ var a =
; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:27] + 1 │ var min, max, mean; min = 1; max = 10; mean = 4; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 10 + ╭─[no_magic_numbers.tsx:1:36] + 1 │ var min, max, mean; min = 1; max = 10; mean = 4; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 4 + ╭─[no_magic_numbers.tsx:1:47] + 1 │ var min, max, mean; min = 1; max = 10; mean = 4; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100n + ╭─[no_magic_numbers.tsx:1:3] + 1 │ f(100n) + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: --100n + ╭─[no_magic_numbers.tsx:1:3] + 1 │ f(-100n) + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100n + ╭─[no_magic_numbers.tsx:1:3] + 1 │ f(100n) + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100 + ╭─[no_magic_numbers.tsx:1:3] + 1 │ f(100) + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 123 + ╭─[no_magic_numbers.tsx:1:23] + 1 │ const func = (param = 123) => {} + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 123 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ const { param = 123 } = sourceObject; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 123 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ const { param = 123 } = sourceObject; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 123 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ const { param = 123 } = sourceObject; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:14] + 1 │ const [one = 1, two = 2] = [] + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:23] + 1 │ const [one = 1, two = 2] = [] + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:22] + 1 │ var one, two; [one = 1, two = 2] = [] + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:31] + 1 │ var one, two; [one = 1, two = 2] = [] + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ class C { foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ class C { foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ class C { foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ class C { foo = -2; } + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:24] + 1 │ class C { static foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:18] + 1 │ class C { #foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:25] + 1 │ class C { static #foo = 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:17] + 1 │ class C { foo = 2 + 3; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:21] + 1 │ class C { foo = 2 + 3; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:11] + 1 │ class C { 2; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ class C { [2]; } + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -1; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1 | 2 | 3; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = 1 | 2 | 3; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:20] + 1 │ type Foo = 1 | 2 | 3; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1 | -1; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = 1 | -1; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:3:11] + 2 │ interface Foo { + 3 │ bar: 1; + · ─ + 4 │ } + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1000 + ╭─[no_magic_numbers.tsx:3:15] + 2 │ enum foo { + 3 │ SECOND = 1000, + · ──── + 4 │ NUM = '0123456789', + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:5:12] + 4 │ NUM = '0123456789', + 5 │ NEG = -1, + · ── + 6 │ POS = +1, + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:6:12] + 5 │ NEG = -1, + 6 │ POS = +1, + · ── + 7 │ } + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:3:19] + 2 │ class Foo { + 3 │ readonly A = 1; + · ─ + 4 │ readonly B = 2; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:4:19] + 3 │ readonly A = 1; + 4 │ readonly B = 2; + · ─ + 5 │ public static readonly C = 3; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:5:33] + 4 │ readonly B = 2; + 5 │ public static readonly C = 3; + · ─ + 6 │ static readonly D = 4; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 4 + ╭─[no_magic_numbers.tsx:6:26] + 5 │ public static readonly C = 3; + 6 │ static readonly D = 4; + · ─ + 7 │ readonly E = -5; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -5 + ╭─[no_magic_numbers.tsx:7:19] + 6 │ static readonly D = 4; + 7 │ readonly E = -5; + · ── + 8 │ readonly F = +6; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 6 + ╭─[no_magic_numbers.tsx:8:19] + 7 │ readonly E = -5; + 8 │ readonly F = +6; + · ── + 9 │ private readonly G = 100n; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 100n + ╭─[no_magic_numbers.tsx:9:27] + 8 │ readonly F = +6; + 9 │ private readonly G = 100n; + · ──── + 10 │ } + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[0]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[-1]; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0xab + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[0xab]; + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5.6e1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[5.6e1]; + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[1 | -2]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:20] + 1 │ type Foo = Bar[1 | -2]; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[1 & -2]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:20] + 1 │ type Foo = Bar[1 & -2]; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:16] + 1 │ type Foo = Bar[1 & number]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:18] + 1 │ type Foo = Bar[((1 & -2) | 3) | 4]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:22] + 1 │ type Foo = Bar[((1 & -2) | 3) | 4]; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:1:28] + 1 │ type Foo = Bar[((1 & -2) | 3) | 4]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 4 + ╭─[no_magic_numbers.tsx:1:33] + 1 │ type Foo = Bar[((1 & -2) | 3) | 4]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:1:28] + 1 │ type Foo = Parameters[2]; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:5:25] + 4 │ type Foo = { + 5 │ [K in keyof Others[0]]: Others[K]; + · ─ + 6 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:3:7] + 2 │ type Other = { + 3 │ [0]: 3; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3 + ╭─[no_magic_numbers.tsx:3:11] + 2 │ type Other = { + 3 │ [0]: 3; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:3:12] + 2 │ type Foo = { + 3 │ [K in 0 | 1 | 2]: 0; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:3:16] + 2 │ type Foo = { + 3 │ [K in 0 | 1 | 2]: 0; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 2 + ╭─[no_magic_numbers.tsx:3:20] + 2 │ type Foo = { + 3 │ [K in 0 | 1 | 2]: 0; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0 + ╭─[no_magic_numbers.tsx:3:24] + 2 │ type Foo = { + 3 │ [K in 0 | 1 | 2]: 0; + · ─ + 4 │ }; + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1; + · ─ + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -2 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -2; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 3n + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 3n; + · ── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: --4n + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -4n; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5.6 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 5.6; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -7.8 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -7.8; + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 0x0a + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 0x0a; + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -0xbc + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -0xbc; + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1e2 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1e2; + · ─── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -3e4 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -3e4; + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5e-6 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 5e-6; + · ──── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -7e-8 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -7e-8; + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 1.1e2 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 1.1e2; + · ───── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -3.1e4 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -3.1e4; + · ────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: 5.1e-6 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = 5.1e-6; + · ────── + ╰──── + + ⚠ typescript-eslint(no-magic-numbers): No magic number: -7.1e-8 + ╭─[no_magic_numbers.tsx:1:12] + 1 │ type Foo = -7.1e-8; + · ─────── + ╰────