{"record":{"id":"b8bd95bcfbb2ca7d","repo":"oxc-project/oxc","slug":"unexpected-class-with-only-static-properties","errorCode":null,"errorMessage":"Unexpected class with only static properties.","messagePattern":"Unexpected class with only static properties\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs","lineNumber":106,"sourceCode":"    typescript,\n    suspicious,\n    dangerous_suggestion,\n    config = NoExtraneousClass,\n    version = \"0.7.0\",\n    short_description = \"Disallow classes used as namespaces.\",\n);\n\nfn empty_class_diagnostic(span: Span, has_decorators: bool) -> OxcDiagnostic {\n    let help = if has_decorators {\n        r#\"Set \"allowWithDecorator\": true in your config to allow empty decorated classes\"#\n    } else {\n        \"Delete this class\"\n    };\n    OxcDiagnostic::warn(\"Unexpected empty class.\").with_label(span).with_help(help)\n}\n\nfn only_static_no_extraneous_class_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected class with only static properties.\")\n        .with_label(span)\n        .with_help(\"Try using standalone functions instead of static methods\")\n}\n\nfn only_constructor_no_extraneous_class_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Unexpected class with only a constructor.\")\n        .with_label(span)\n        .with_help(\"Try replacing this class with a standalone function or deleting it entirely\")\n}\n\nimpl Rule for NoExtraneousClass {\n    fn from_configuration(value: serde_json::Value) -> Result<Self, serde_json::error::Error> {\n        DefaultRuleConfig::<Self>::from_value(value).map(DefaultRuleConfig::into_inner)\n    }\n\n    fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {\n        let AstKind::Class(class) = node.kind() else {\n            return;","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/typescript/no_extraneous_class.rs#L88-L124","documentation":"Second diagnostic of no-extraneous-class: a class whose every member is static is a namespace in disguise, so the rule flags it. The help at no_extraneous_class.rs:108-111 recommends standalone functions instead of static methods.","triggerScenarios":"`class MathUtils { static add(...) {} static sub(...) {} }` - every class element is a static member with no instance state.","commonSituations":"Utility bags written out of Java or C# habit, helpers grouped into a class purely for namespacing or editor discoverability.","solutions":["Export plain functions from the module instead","Replace the class with a frozen object literal or a TS namespace","Add an instance member if the class genuinely models state","Disable inline when the grouping is deliberate"],"exampleFix":"// before\nclass Str {\n  static trim(s: string) { return s.trim(); }\n}\nconst t = Str.trim(x);\n\n// after\nexport function trim(s: string) { return s.trim(); }\nconst t = trim(x);","handlingStrategy":"validation","validationCode":"npx oxlint --deny-warnings .","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to exported functions for utility code; reach for a class only when there is instance state","Codemod static utility classes to module functions early in a migration","Flag all-static classes in code review as a namespace smell"],"tags":["typescript","lint","class","static","refactor"],"backgroundTag":"extraneous-class","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}