問題の発生
このエラーは、abstract修飾子が付けられたクラスに対してnewキーワードを使用しようとしたときに発生します。抽象クラスは、スケッチや設計図のようなものだと考えてください。関連するクラス群が何をすべきかを定義しますが、それ自体は完成品ではありません。未完成の状態であるため、TypeScriptはそれを実際のオブジェクトに変換することを制限しています。
ロギングシステムを構築している場面を想像してみましょう。次のようなコードでエラーが発生する可能性があります。
abstract class Logger {
abstract log(message: string): void;
info(message: string) {
console.log(`[INFO]: ${message}`);
}
}
// ❌ TypeScriptエラー: Cannot create an instance of an abstract class.
const myLogger = new Logger();
エラーが発生する理由
abstractキーワードは、セーフガードとして機能します。抽象クラスには、実際のコードを持たないメソッド(抽象メソッド)が含まれることが多いため、インスタンスを作成するのは危険です。もしTypeScriptがこれを許可してしまうと、実行時に実装されていないメソッドを呼び出した瞬間にアプリケーションがクラッシュする可能性があります。たとえクラスに完全に記述されたメソッドしか含まれていなくても、abstractタグはコンパイラに対して「このクラスは継承専用である」ことを伝えます。
具体的な解決策
1. 具体的なサブクラスを実装する
標準的な修正方法は、抽象ベースクラスを継承する「具象(concrete)」クラスを作成することです。この子クラスは、不足しているロジックを実装することで空白を埋めます。これは、オブジェクト指向のTypeScriptプロジェクトの90%で見られる最も一般的なアプローチです。
abstract class Shape {
abstract getArea(): number;
}
// ✅ 解決策: 具体的な実装を作成する
class Square extends Shape {
constructor(private width: number) {
super();
}
getArea(): number {
return this.width * this.width;
}
}
// ベースクラスではなくサブクラスをインスタンス化する
const mySquare = new Square(5);
console.log(mySquare.getArea()); // 出力: 25
2. abstract修飾子を削除する
そのクラスが本当に抽象クラスである必要があるか自問してみてください。ベースクラスを継承するよりも、直接使用したい場面が多いのであれば、おそらくabstractキーワードは不要です。これを削除することで、クラスは通常のインスタンス化可能なクラスになります。
// 変更前: abstract class ApiClient { ... }
// 変更後: 通常のクラス
class ApiClient {
fetchData(endpoint: string) {
return fetch(`https://api.example.com/${endpoint}`);
}
}
const client = new ApiClient(); // 正常に動作します
3. ファクトリ関数とジェネリクスの扱い
クラスをファクトリ関数や依存性の注入(DI)コンテナに渡す際に、この壁に突き当たることがあります。関数がコンストラクタを期待しているのに抽象クラスを渡すと、型が一致しません。これは、NestJSのようなフレームワークやカスタムプラグインを構築する際によく発生します。
abstract class BaseService {}
class AuthService extends BaseService {}
// このヘルパーはインスタンス化可能なクラスを期待します
function createInstance<T>(Ctor: new () => T): T {
return new Ctor();
}
// ❌ エラー: BaseServiceは抽象クラスです
createInstance(BaseService);
// ✅ 成功: AuthServiceは具象クラスです
createInstance(AuthService);
修正を確認する方法
修正できたと思い込まず、以下の3つのステップで確認してください。
- IDEを確認する: 具体的なサブクラスに切り替えると、VS Codeの
newキーワードの下にある赤い波線がすぐに消えるはずです。 - コンパイラを実行する: ターミナルで
npx tscを実行します。「Cannot create an instance」エラーなしでビルドが完了すれば、型定義は適切です。 - ランタイムロジックをテストする: コードを実行し(例:
node dist/index.js)、サブクラスのメソッドが意図通りにロジックを実行することを確認します。
将来のための設計アドバイス
アーキテクチャをクリーンに保つために、以下の3つのパターンを検討してください。
- インターフェース vs 抽象クラス: 共有コードがなく、形状(型)のみを定義したい場合は、
interfaceを使用してください。インターフェースは実行時には消滅するため、バンドルサイズを小さく抑えられます。 - 「Base」プレフィックス: クラス名を
BaseRepositoryやBaseControllerのように命名することで、そのクラスが単体で使用されることを意図していないという明確な視覚的合図を他の開発者に提供できます。 - コンストラクタのアクセス制御: 抽象クラス内であっても、
protected constructor()を使用できます。これにより、サブクラスのみがsuper()を呼び出せるようになり、言語レベルで抽象的な振る舞いを強化できます。

