可通过在父类构造函数中判断 new.target === 父类并抛错,禁止直接实例化父类,仅允许子类继承后调用 super() 实例化;new.target 指向当前 new 调用的构造函数,子类实例化时其值为子类而非父类。

可以通过在类的构造函数中检查 new.target 是否等于该类本身,来禁止直接调用 new MyClass(),只允许子类继承后实例化。
基本原理:new.target 的行为
new.target 在构造函数中指向当前被 new 调用的构造函数。如果直接实例化父类,new.target === ParentClass;如果通过子类实例化,new.target 指向子类,不等于父类。
- 父类构造函数中若发现
new.target === ParentClass,说明被直接 new,应抛出错误 - 子类继承后调用
super(),此时new.target是子类,检查不成立,允许执行
实现方式:在 constructor 中判断并 throw
只需在父类构造函数开头加入判断逻辑:
class AbstractBase {
constructor() {
if (new.target === AbstractBase) {
throw new TypeError('AbstractBase cannot be instantiated directly');
}
// 其他初始化逻辑
}
}
class Concrete extends AbstractBase {
constructor() {
super(); // 此时 new.target 是 Concrete,不等于 AbstractBase,不会报错
}
}
// ✅ 正确:通过子类实例化
new Concrete();
// ❌ 报错:直接实例化父类
new AbstractBase(); // TypeError: AbstractBase cannot be instantiated directly
注意点与常见误区
这个限制仅在运行时生效,TypeScript 的类型系统无法自动识别这种“抽象性”,需手动加 abstract 声明或 JSDoc 注释提升可读性。
- 不能仅靠
new.target实现真正的“抽象类”语义(如强制子类实现某方法),它只管实例化路径 - 如果父类有带参数的构造函数,确保子类
super(...args)正确传递,否则可能因参数校验失败而中断 - 箭头函数没有
new.target,所以不能用于构造逻辑;必须使用普通函数或 class 构造器
进阶:配合 abstract 关键字(TS)或文档约定
TypeScript 支持 abstract class,虽不阻止 JS 运行时直接 new,但能配合 new.target 提供双重保障:
abstract class AbstractBase {
constructor() {
if (new.target === AbstractBase) {
throw new TypeError('AbstractBase is abstract and cannot be instantiated');
}
}
abstract doSomething(): void;
}
这样既能在编辑器中获得类型提示,又在运行时拦截非法调用,更健壮。










