コンストラクタのジレンマ
ビルドが失敗し、ターミナルには super と this に関する謎めいたメッセージが表示されます。コアサービスをリファクタリングしようとしただけなのに、IDEは赤色の波線で埋め尽くされています。このエラーは、TypeScript開発者にとって避けては通れない登竜門のようなものです。
'super' must be called before accessing 'this' in the constructor of a derived class.
モダンな JavaScript と TypeScript は、厳格な継承プロトコルに従います。子クラスが親クラスを修正する前に、親クラスが存在していなければなりません。super() を呼び出す前に this に触れようとすると、インスタンスがまだ初期化されていないため、エンジンによってブロックされます。これは、まだ骨組みもできていない家の部屋を塗装しようとしているようなものです。
実際の失敗例
DatabaseService を構築していると仮定しましょう。特定のユーザーロジックを処理するためにベースクラスを拡張したいのですが、プロパティをセットするタイミングが早すぎた場合を考えます。
class BaseService {
protected apiVersion: number;
constructor(version: number) {
this.apiVersion = version;
}
}
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
// ❌ エラー: super() の前に this.endpoint にアクセスしている
this.endpoint = '/users';
super(version);
}
}
この this.endpoint への代入がエラーを引き起こします。TypeScriptは、BaseService が自身の内部状態をセットアップする機会を得る前に、UserService インスタンスを操作しようとしていることを検知します。
なぜエンジンによってブロックされるのか
ES6のクラスでは、this キーワードはコンストラクタが開始された瞬間に魔法のように使えるわけではありません。代わりに、super() の呼び出しによって this が現在のインスタンスにバインドされます。その関数が完了するまで、this は未初期化のままです。
super() の前に this へのアクセスを許可するのは危険です。現在 undefined である親のプロパティに依存するメソッドを呼び出してしまう可能性があるからです。この制限により、古い JavaScript のパターンでよく見られた実行時クラッシュのカテゴリを未然に防いでいます。
30秒でできる修正:順序の入れ替え
ほとんどの場合、解決策は簡単です。super() をコンストラクタの最上部に移動してください。これにより、装飾を始める前に土台がしっかりしていることが保証されます。
class UserService extends BaseService {
private endpoint: string;
constructor(version: number) {
super(version); // ✅ 土台が構築された
this.endpoint = '/users'; // ✅ 'this' を安全に使用できる
}
}
super 呼び出し前のロジックの管理
super() 呼び出しに「渡すため」の値を計算する必要がある場合はどうすればよいでしょうか? そのために this を使うことはできませんが、方法がないわけではありません。
間違った方法
constructor(config: any) {
this.token = this.decrypt(config.key); // エラー: this.decrypt はまだ利用できない
super(this.token);
}
正しい方法:ローカル変数と静的メソッド
ローカル変数または static ヘルパー関数を使用します。これらはインスタンスではなくクラス自体に存在するため、機能させるために this を必要としません。
class UserService extends BaseService {
constructor(config: any) {
// 1. ローカル定数を使用してロジックを実行
const decryptedToken = UserService.decrypt(config.key);
// 2. 結果を親に渡す
super(decryptedToken);
// 3. インスタンスのセットアップを完了
console.log("ユーザーサービスがアクティブです。");
}
private static decrypt(key: string): string {
return key.split('').reverse().join(''); // 簡略化したロジック
}
}
検証とテスト
赤い線が消えただけで安心しないでください。次の3つのステップで修正を確認しましょう。
- コンパイラの実行:
npx tscを実行します。エラーが返されなければ、継承チェーンの構文は健全です。 - 実行時エラーの確認: ブラウザのコンソールに
ReferenceError: Must call super constructor...と表示される場合は、プロパティの初期化子に隠れたthisへのアクセスがある可能性があります。 - ユニットテスト初期化: クラスをインスタンス化するテストケースを作成します。親と子の両方のプロパティ(例:
this.apiVersionとthis.endpoint)が期待通りであることを確認してください。
「superを先に」というルールを守ることで、オブジェクト指向のコードを予測可能な状態に保つことができます。初期化前のロジックには静的ヘルパーを使用すれば、このエラーに二度と悩まされることはないでしょう。

