oxc-project/oxc · error
Unexpected re-assignment of class {name}
Error message
Unexpected re-assignment of class {name} What it means
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).
Source
Thrown at crates/oxc_linter/src/rules/eslint/no_class_assign.rs:10
use oxc_ast::{AstKind, ast::BindingIdentifier};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::AstNode;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule};
fn no_class_assign_diagnostic(name: &str, decl_span: Span, assign_span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("Unexpected re-assignment of class {name}"))
.with_help("Use a different variable name instead of re-assigning the class declaration.")
.with_labels([
decl_span.label(format!("{name} is declared as class here")),
assign_span.label(format!("{name} is re-assigned here")),
])
}
#[derive(Debug, Default, Clone)]
pub struct NoClassAssign;
declare_oxc_lint!(
/// ### What it does
///
/// Disallow reassigning class variables.
///
/// This rule can be disabled for TypeScript code, as the TypeScript compiler
/// enforces this check.
///View on GitHub (pinned to e1e7af627c)
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.
Example fix
// before
class Router {}
Router = upgrade(Router); // TypeError in strict mode
// after
class Router {}
const UpgradedRouter = upgrade(Router); Defensive patterns
Strategy: validation
Validate before calling
// Flag rebinding of a declared class name in review gates
const redecl = /^\s*class\s+(\w+)/gm;
for (const [, name] of src.matchAll(redecl)) {
if (new RegExp(`^\\s*${name}\\s*(=[^=]|\\*=|\\+=|\+\+|--)` , 'm').test(src)) block(name);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Do not use `arguments.{method_name}`.
- Unexpected return statement in constructor.
- Empty constructors are unnecessary
- Redundant super call in constructor
- Literals should be exposed using readonly fields.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/973d01285505eca9.
Report an issue: GitHub.