Next.js中子类属性无法通过父类构造函数赋值的解决方法咨询
在Next.js里运行这段代码,控制台一直输出B { first_name: undefined }:
class A { // A: 基类Model constructor(source){ Object.keys(source) .forEach(key => { if(!this[key]){ this[key] = source[key]; } }); } } class B extends A{ // B: 子类User first_name: string; } let v = new B({first_name: "nof"}); console.log(v) // 输出:B { first_name: undefined }
但同样的代码在普通非Next.js项目里能正常运行。现在想在不声明子类B的构造函数的前提下解决这个问题。
参考示例(表现出类似异常):
class A { constructor(){ this.init() } init(){ this["n1"] = "nof"; this["n2"] = "nof"; } } class B extends A{ n1: string; } let v = new B(); console.log(v) // 输出:B { n1: undefined, n2: 'nof' }
这是Next.js默认开启了TypeScript的useDefineForClassFields编译选项导致的。这个选项在TS 3.7+引入,Next.js项目里默认启用。
开启该选项后,子类中声明的字段(比如B类的first_name: string)会被当作运行时实际存在的类字段,在实例化时自动初始化为undefined。父类A的构造函数里判断!this[key]时,this[key]已经是undefined(但字段已经存在),所以条件不成立,不会执行赋值操作,最终字段值还是undefined。
而普通非Next.js项目里,这个选项通常没开启,类字段只是类型注解,不会在运行时生成初始化逻辑,所以父类构造函数能正常给字段赋值。
方法1:修改TypeScript配置
在项目根目录的tsconfig.json里,把useDefineForClassFields设为false:
{ "compilerOptions": { "useDefineForClassFields": false, // 保留其他原有配置 } }
改完重启Next.js开发服务,父类构造函数就能正常给子类字段赋值了。
方法2:调整父类的赋值逻辑
不用改配置,直接修改父类A的构造函数,去掉if(!this[key])的判断,强制赋值:
class A { // A: 基类Model constructor(source){ Object.keys(source) .forEach(key => { this[key] = source[key]; // 移除判断,直接覆盖字段值 }); } }
这样不管子类有没有提前初始化字段,都会被source里的值覆盖,达到预期效果。
方法3:给子类字段设初始占位值(不推荐)
如果允许修改子类但不声明构造函数,可以给子类字段设置一个非undefined的初始值,让父类的判断条件成立:
class B extends A{ // B: 子类User first_name: string | null = null; }
但这种方法需要修改每个子类的字段,维护成本高,不如前两种方法实用。
内容的提问来源于stack exchange,提问作者naoufal bardouni

