TypeScript中使用类装饰器免new实例化类,如何消除调用报错?
问题背景
我们想在TypeScript中实现无需new关键字就能实例化类的功能。参考相关装饰器代码实现后,JavaScript运行正常,但TypeScript会提示类不可调用的错误——即便实际运行时能正常生成实例。
现有代码
类装饰器代码
var classy = function classy() { return function (Class) { var _Class = function _Class() { for (var _len = arguments.length, rest = Array(_len), _key = 0; _key < _len; _key++) { rest[_key] = arguments[_key]; } return new(Function.prototype.bind.apply(Class, [null].concat(rest)))(); }; _Class.prototype = Class.prototype; return _Class; }; };
TypeScript使用代码
@classy() class Someone{ constructor(private name: string){} } debugger let some1 = new Someone("john"); let some2 = Someone("joe") // 实际能生成Someone实例,但TypeScript报错:类不可调用
解决方法
要消除TypeScript的报错,核心是给装饰器添加正确的类型定义,让TypeScript明确:被装饰后的类既可以用new实例化,也可以直接调用。
步骤1:给classy装饰器补充TypeScript类型
修改装饰器代码,添加类型声明并简化逻辑:
// 定义普通类构造函数类型 type Constructor<T = {}> = new (...args: any[]) => T; // 合并构造函数与可调用函数的类型 type CallableConstructor<T> = Constructor<T> & ((...args: any[]) => T); function classy(): <T extends Constructor>(target: T) => CallableConstructor<T> { return function <T extends Constructor>(Class: T) { const _Class = function (...args: any[]) { return new (Class.bind(null, ...args))(); } as CallableConstructor<T>; // 继承原类原型链 _Class.prototype = Class.prototype; // 复制原类的静态属性/方法(避免丢失静态成员) Object.assign(_Class, Class); return _Class; }; }
步骤2:修改使用代码
现在使用装饰器后,TypeScript能正确识别类的可调用特性,不会再报错:
@classy() class Someone{ constructor(private name: string){} } let some1 = new Someone("john"); let some2 = Someone("joe"); // TypeScript不再报错,确认这是Someone实例 console.log(some1 instanceof Someone); // true console.log(some2 instanceof Someone); // true
原理说明
Constructor<T>定义了标准类构造函数的类型,要求必须用new调用。CallableConstructor<T>将构造函数类型与普通函数类型合并,让TypeScript知晓这个类支持两种实例化方式。- 装饰器返回
CallableConstructor<T>类型,覆盖了原类的默认类型声明,从根源消除报错。 Object.assign(_Class, Class)用于保留原类的静态成员,避免装饰后丢失静态属性或方法。
内容的提问来源于stack exchange,提问作者tru7
相关产品推荐
相关产品推荐

