如何修复Angular11升级后的类构造函数无'new'调用报错
Angular 11环境下动态实例生成报错适配指南
问题现象
原有动态实例生成逻辑在Angular 7版本可正常运行,升级至Angular 11后触发运行时报错:ERROR TypeError: Class constructor DyanamicClasses cannot be invoked without 'new'
触发问题的核心实例化方法代码:
static getInstance<T>(DyanamicClasses, name: string, ...args: any[]) : T { const instance = Object.create(DyanamicClasses[name].prototype); instance.constructor.apply(instance, args); return <T> instance; }
涉及的类定义存放于DyanamicClasses命名空间下,类的写法本身无语法问题:
export namespace DyanamicClasses{ export class HallmarkMxFieldEditStatusAccessor implements IFieldEditStatus{ item: any; fieldDefinition: FieldDefinition; constructor(public _item: any, public _fieldDefinition: FieldDefinition){ this.item = _item; this.fieldDefinition = _fieldDefinition; } } }
报错根因
- Angular 7版本默认TypeScript编译目标为ES5,class语法会被转译为ES5的普通构造函数,这类构造函数支持无new调用、apply/call绑定执行的操作,原有实例化逻辑可正常运行。
- 升级Angular 11后,默认TypeScript编译目标调整为ES2015(ES6),编译输出的是原生ES6 class。根据ES6语言规范,原生class的构造函数必须通过new关键字调用,不允许直接通过apply/call方式绑定上下文执行,原有手动创建原型、apply调用构造函数的写法直接违反规范,因此触发报错。
适配修复方案
不需要回退编译目标、也不需要修改原有命名空间下的类定义,仅调整实例化方法的实现逻辑即可,两种可直接落地的实现:
方案1:直接通过new关键字实例化(推荐)
对目标构造函数做类型断言后,直接使用展开语法传递参数调用new,完全符合ES6 class的调用规范:
static getInstance<T>(DyanamicClasses, name: string, ...args: any[]) : T { // 断言目标对象为可被new调用的构造函数类型 const targetConstructor = DyanamicClasses[name] as new (...args: any[]) => T; // 直接通过new执行构造流程 return new targetConstructor(...args); }
方案2:使用Reflect.construct实现
如果需要兼容动态参数的场景,可使用ES2015原生提供的Reflect.construct方法,该方法原生支持按new流程调用构造函数,和原有手动实现的实例化逻辑语义完全一致:
static getInstance<T>(DyanamicClasses, name: string, ...args: any[]) : T { const targetConstructor = DyanamicClasses[name]; // Reflect.construct会自动触发new调用流程,兼容原生ES6 class return Reflect.construct(targetConstructor, args) as T; }
注意事项
- 不建议为了兼容老写法将tsconfig的编译目标回退为ES5,会直接降低Angular应用的运行性能、增大打包体积,违背版本升级的初衷。
- 原有命名空间下的类定义不需要做任何修改,本次报错仅为实例化逻辑的兼容性问题,类本身的实现完全符合ES2015的语法规范。
内容的提问来源于stack exchange,提问作者Masum Mollik
相关产品推荐
相关产品推荐

