The Constructor Catch-22
Your build fails. The terminal spits out a cryptic message about super and this. You were just trying to refactor a core service, but now your IDE is covered in red squiggles. This error is a classic rite of passage for TypeScript developers.
'super' must be called before accessing 'this' in the constructor of a derived class.
Modern JavaScript and TypeScript follow a strict inheritance protocol. The parent class must exist before the child class can modify it. If you try to touch this before calling super(), the engine blocks you because the instance hasn't actually been initialized yet. You are essentially trying to paint a room in a house that hasn't been framed.
A Real-World Failure
Suppose you are building a DatabaseService. You want to extend a base class to handle specific user logic, but you try to set a property too early:
class BaseService {
protected apiVersion: number;
constructor(version: number) {
this.apiVersion = version;
}
}
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
// โ Error: this.endpoint accessed before super()
this.endpoint = '/users';
super(version);
}
}
That this.endpoint assignment triggers the error. TypeScript sees you interacting with the UserService instance before the BaseService has had a chance to set up its own internal state.
Why the Engine Blocks You
In ES6 classes, the this keyword is not magically available the moment the constructor starts. Instead, the super() call is what binds this to the current instance. Until that function finishes, this remains uninitialized.
Allowing access to this before super() would be dangerous. You might call a method that relies on a parent property which is currently undefined. This restriction prevents a whole category of runtime crashes that used to plague older JavaScript patterns.
The 30-Second Fix: Reordering
Most of the time, the solution is trivial. Move super() to the very top of your constructor. This ensures the foundation is solid before you start decorating.
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
super(version); // โ
Foundation laid
this.endpoint = '/users'; // โ
Safe to use 'this'
}
}
Managing Logic Before the Super Call
What if you need to calculate a value to pass into the super() call? You can't use this to do it, but you aren't stuck.
The Wrong Way
constructor(config: any) {
this.token = this.decrypt(config.key); // Error: this.decrypt is not available yet
super(this.token);
}
The Right Way: Local Variables and Static Methods
Use local variables or static helper functions. These exist on the class itself, not the instance, so they don't require this to function.
class UserService extends BaseService {
constructor(config: any) {
// 1. Perform logic using local constants
const decryptedToken = UserService.decrypt(config.key);
// 2. Pass the result to the parent
super(decryptedToken);
// 3. Complete instance setup
console.log("User service active.");
}
private static decrypt(key: string): string {
return key.split('').reverse().join(''); // Simplified logic
}
}
Verification and Testing
Don't just trust the lack of red lines. Verify your fix with these three steps:
- Run the Compiler: Hit
npx tsc. If it returns no errors, your inheritance chain is syntactically sound. - Check Runtime Errors: If you see
ReferenceError: Must call super constructor...in your browser console, you likely have a hiddenthisaccess in a property initializer. - Unit Test Initialization: Create a test case that instantiates the class. Confirm that both parent and child properties (e.g.,
this.apiVersionandthis.endpoint) are exactly what you expect.
Sticking to the "super first" rule keeps your object-oriented code predictable. Use static helpers for pre-initialization logic and you'll never see this error again.

