this/super Used Before super() in Derived Constructor

ID

javascript.no_this_before_super

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

reliability, suspicious-control-flow

Description

Reports any reference to this or super.foo inside a derived-class constructor (a class with an extends clause) that appears textually before the constructor’s first super(…​) call. ECMAScript binds this in a derived constructor only after super(…​) returns; reading or writing this (or accessing super.foo) before that point throws a ReferenceError at runtime: "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".

This is a v1 implementation that uses textual position rather than full control-flow analysis. References inside a nested non-arrow function are skipped because they do not run synchronously with the constructor body. References inside arrow functions are still considered, because arrows do not bind their own this or super.

If the constructor has no super(…​) call at all, this rule does not fire — the sister rule constructor_super covers that case.

Rationale

  • Pre-super() access to this is an immediate ReferenceError in modern engines. The error is masked when the failing branch is rarely exercised in tests.

  • super.method() before super() reads from the prototype chain through the not-yet-initialized derived instance and is equally rejected by the engine.

  • Even if the runtime semantics were lenient, the order is so surprising for readers that putting super(…​) first is the only sensible style.

// Bad
class Greeter extends Base {
  constructor(name) {
    this.name = name;          // ReferenceError before super()
    super();
  }
}

class Wrapper extends Base {
  constructor() {
    super.init();              // accessing super before super()
    super();
  }
}

Remediation

Place super(…​) at the top of the constructor body, before any reference to this or super.

// Good
class Greeter extends Base {
  constructor(name) {
    super();
    this.name = name;
  }
}

If you need to compute an argument for super(…​) from constructor parameters, do so with local variables — not via this:

// Good
class Tagged extends Base {
  constructor(arg) {
    const tag = arg ? String(arg) : "default";
    super(tag);
    this.tag = tag;
  }
}