{"record":{"id":"973d01285505eca9","repo":"oxc-project/oxc","slug":"unexpected-re-assignment-of-class-name","errorCode":null,"errorMessage":"Unexpected re-assignment of class {name}","messagePattern":"Unexpected re-assignment of class (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/oxc_linter/src/rules/eslint/no_class_assign.rs","lineNumber":10,"sourceCode":"use oxc_ast::{AstKind, ast::BindingIdentifier};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_semantic::AstNode;\nuse oxc_span::Span;\n\nuse crate::{context::LintContext, rule::Rule};\n\nfn no_class_assign_diagnostic(name: &str, decl_span: Span, assign_span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\"Unexpected re-assignment of class {name}\"))\n        .with_help(\"Use a different variable name instead of re-assigning the class declaration.\")\n        .with_labels([\n            decl_span.label(format!(\"{name} is declared as class here\")),\n            assign_span.label(format!(\"{name} is re-assigned here\")),\n        ])\n}\n\n#[derive(Debug, Default, Clone)]\npub struct NoClassAssign;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Disallow reassigning class variables.\n    ///\n    /// This rule can be disabled for TypeScript code, as the TypeScript compiler\n    /// enforces this check.\n    ///","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_class_assign.rs#L1-L28","documentation":"Diagnostic from the oxlint rule `no-class-assign` (crates/oxc_linter/src/rules/eslint/no_class_assign.rs). It fires when the binding of a class declaration is reassigned after declaration, and emits two labels: where the class is declared and where it is re-assigned. Class bindings behave like `const` — reassigning one throws a TypeError in strict mode (which ES modules and classes always are).","triggerScenarios":"An AssignmentExpression (or update/compound assignment) whose target is a Reference to a SymbolId declared by a ClassDeclaration — e.g. `class A {}` later followed by `A = 1;` or `A += x`. The diagnostic carries both the decl_span and assign_span labels.","commonSituations":"Using the class name as a mutable namespace or registry slot (`Foo = extendedVersion`); decorator-style monkey-patching; strict-mode ESM code where this pattern throws at runtime, not just at lint time.","solutions":["Use a different variable for the new value: `class A {}` then `const B = createReplacement();`.","If the binding must change, declare with `let` via a class expression: `let A = class A {};`.","Mutate static members instead of rebinding: `A.handler = newHandler;`.","Suppress inline only if the reassignment is dead legacy code slated for removal."],"exampleFix":"// before\nclass Router {}\nRouter = upgrade(Router); // TypeError in strict mode\n\n// after\nclass Router {}\nconst UpgradedRouter = upgrade(Router);","handlingStrategy":"validation","validationCode":"// Flag rebinding of a declared class name in review gates\nconst redecl = /^\\s*class\\s+(\\w+)/gm;\nfor (const [, name] of src.matchAll(redecl)) {\n  if (new RegExp(`^\\\\s*${name}\\\\s*(=[^=]|\\\\*=|\\\\+=|\\+\\+|--)` , 'm').test(src)) block(name);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat class bindings as const; use static members or factory functions for variation.","If rebinding is required, declare `let A = class A {}` from the start.","Rely on no-class-assign in CI to catch regressions before runtime TypeError."],"tags":["lint","class","strict-mode","const-binding","oxlint"],"backgroundTag":"class-reassignment","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"}