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

TypeScript抽象类继承时构造函数配置子类的问题及解决方案

问题解答

1. TypeScript/JavaScript 运行该示例的底层逻辑

这个问题的核心是JS/TS的类实例初始化顺序和PHP存在本质差异,ES标准定义的继承类初始化流程如下:

  • 执行new SportCar()时,会首先调用父类Car的构造函数,此时子类SportCar的实例属性还未执行默认赋值
  • 父类构造函数内调用this.initCar(),此时this指向SportCar实例,方法执行后this.engine被赋值为V8
  • 父类构造函数执行完毕,控制权回到子类初始化流程,此时才会执行子类声明的属性默认赋值逻辑:private engine = '',直接将之前赋值的V8覆盖为空字符串
  • 后续调用start()方法时,读取到的engine就是空值,所以出现不符合预期的输出

2. 场景解决方案

以下是可直接落地的改造方案:

  • 方案1:移除子类属性的默认赋值(最适配当前抽象类设计)
    仅声明属性类型,通过非空断言告知TypeScript该属性会在初始化流程中被正确赋值,避免多余的默认值覆盖操作:

    class SportCar extends Car {
        // 不设置默认空值,用!声明该属性不会为undefined
        private engine!: string
        
        initCar(): void {
            this.engine = 'V8'
        }
    
        start() {
            console.log(`I'm starting the ${this.engine} engine.`)
        }
    }
    

    改造后运行即可输出预期结果:I'm starting the V8 engine.

  • 方案2:避免在父类构造函数中调用可重写方法(符合JS/TS最佳实践)
    构造函数阶段调用子类可重写的方法极易触发初始化顺序问题,推荐改用工厂方法完成初始化:

    abstract class Car {
        // 构造函数不再调用initCar
        constructor() {}
    
        abstract initCar(): void
        abstract start(): void
    
        // 静态工厂方法统一完成实例初始化
        static create<T extends Car>(this: new () => T): T {
            const instance = new this()
            instance.initCar()
            return instance
        }
    }
    
    // 子类不用改原有逻辑
    class SportCar extends Car {
        private engine = ''
        
        initCar(): void {
            this.engine = 'V8'
        }
    
        start() {
            console.log(`I'm starting the ${this.engine} engine.`)
        }
    }
    
    // 通过工厂方法创建实例
    const sportCar = SportCar.create()
    sportCar.start()
    
  • 方案3:子类构造函数显式控制初始化顺序
    子类重写构造函数,在super()执行完成后再调用初始化方法,父类构造函数中移除初始化调用即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 06:12:02