You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何限制build方法仅接收Plant抽象类的非抽象子类作为参数并实例化

解决TypeScript中抽象类实例化的类型限制问题

嘿,这个问题其实不用靠运行时判断来解决!TypeScript本身就支持在类型层面限制build方法只能接收可实例化的Plant子类,从编译阶段就把错误拦下来,比运行时判断更优雅。

问题根源

你现在的build方法参数类型是typeof Plant,这个类型包含了抽象类Plant本身——而抽象类的构造函数是不能被直接new的,所以当你传入Plant(虽然你现在没这么做,但类型上允许)或者不小心定义了另一个抽象子类时,就会触发报错。

类型层面的解决方案:用构造签名限制

我们可以把build方法的参数类型改成构造签名,明确要求传入的类必须是能通过new (cell: Cell)创建实例的非抽象类:

class Cell {
    // 修改参数类型为构造签名
    build(c: new (cell: Cell) => Plant) {
        if (c.cost <= game.money) {
            this.plant = new c(this); // 现在不会再报抽象类实例化的错误了!
            game.money -= c.cost;
        }
    }
}

这个new (cell: Cell) => Plant的意思是:

  • 这是一个构造函数,接收一个Cell类型的参数
  • 调用它会返回一个Plant类型的实例

而抽象类的构造函数不符合这个签名,因为TypeScript会明确禁止抽象类被直接实例化,所以如果有人尝试把Plant本身传给build,编译阶段就会报错:

Cannot assign an abstract constructor type to a non-abstract constructor type.

完美实现了你想要的“仅接收继承自Plant的非抽象类”的限制!

优化你的植物类代码

另外,你的Sunflower类可以简化一下,父类已经声明了cell属性,子类不需要重复定义;如果构造函数只是调用super,也可以省略:

class Sunflower extends Plant {
    public static dmg = 0;
    public static cost = 50;
    // 不需要重复声明cell,也不需要写constructor,默认会调用super(cell)
}

可选的运行时兜底(非必需)

如果你担心极端情况(比如绕过TypeScript类型检查的JS代码调用),可以加个简单的运行时判断兜底,比如检查实例是否是Plant的实例:

build(c: new (cell: Cell) => Plant) {
    if (c.cost <= game.money) {
        const newPlant = new c(this);
        if (newPlant instanceof Plant) {
            this.plant = newPlant;
            game.money -= c.cost;
        }
    }
}

不过这个兜底在正常的TypeScript开发中几乎用不上,因为类型检查已经把问题解决了。

完整修改后的代码

abstract class Plant {
    public static dmg: number;
    public static cost: number;
    constructor(public cell: Cell) {} // 用public参数简化属性声明
}

class Sunflower extends Plant {
    public static dmg = 0;
    public static cost = 50;
}

class Cell {
    plant?: Plant;
    build(c: new (cell: Cell) => Plant) {
        // 假设game是全局可用的变量
        if (c.cost <= game.money) {
            this.plant = new c(this);
            game.money -= c.cost;
        }
    }
}

// 测试调用:完全没问题
let c = new Cell();
c.build(Sunflower);

// 如果尝试传Plant本身,编译阶段就会报错
// c.build(Plant); // 报错:Cannot assign an abstract constructor type to a non-abstract constructor type.

这样既满足了“新增植物类型只需配置静态属性”的需求,又从根源上避免了抽象类实例化的错误,而且类型安全!

内容的提问来源于stack exchange,提问作者0brine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:48:13