TypeScript类装饰器返回高阶函数时无法访问静态值问题
类装饰器返回高阶函数时的静态成员访问异常问题解决
这是个很常见的TypeScript类装饰器陷阱——当装饰器返回一个高阶函数(替代原类的构造函数)时,原类标识符的指向会被修改,导致类内部访问静态成员出现异常。咱们先看你的问题代码和输出:
问题代码
function Bar(FooClass: any) { console.log('in Bar decorator'); return function (args) { console.log('in Bar high order function: ', args); let foo = new FooClass(); } } @Bar class Foo { a = 1; static b = 2; constructor(args) { console.log('in Foo constructor') console.log('Foo instance a: ', this.a); console.log('Foo static b: ', Foo.b); } } new Foo(123);
运行输出
in Bar decorator in Bar high order function: 123 in Foo constructor Foo instance a: 1 Foo static b: undefined
问题原因
当你用@Bar装饰Foo类时,TypeScript会把Foo替换成Bar(Foo)返回的那个函数。也就是说,现在代码里的Foo标识符指向的是装饰器返回的匿名函数,而不是原来定义的Foo类。所以在Foo的构造函数里访问Foo.b,其实是在访问这个匿名函数的b属性——它当然是undefined,因为我们没给它加这个静态成员。
而你提到在装饰器里可以通过FooClass.b访问,是因为FooClass参数接收的是原来的Foo类,它的静态成员还在。
解决方案
这里有两种常用的解决思路:
方案1:在构造函数中通过this.constructor访问静态成员
既然Foo标识符已经被替换,我们可以通过实例的constructor属性获取到真正的原类(因为实例是由原FooClass创建的),再访问它的静态成员:
function Bar(FooClass: typeof Foo) { console.log('in Bar decorator'); return function (args: any) { console.log('in Bar high order function: ', args); return new FooClass(args); // 注意:这里要返回实例,原代码遗漏了这一步 } } @Bar class Foo { a = 1; static b = 2; constructor(args: any) { console.log('in Foo constructor') console.log('Foo instance a: ', this.a); // 用this.constructor获取原类,加上类型断言避免TS报错 console.log('Foo static b: ', (this.constructor as typeof Foo).b); } } new Foo(123);
方案2:在装饰器中复制原类的静态成员到新构造函数
如果不想修改类内部的代码,可以在装饰器里把原类的所有静态成员复制到返回的新构造函数上,让Foo标识符指向的新函数也拥有这些静态属性:
function Bar(FooClass: typeof Foo) { console.log('in Bar decorator'); // 创建新构造函数,并指定类型为原类的类型 const WrappedFoo = function (args: any) { console.log('in Bar high order function: ', args); return new FooClass(args); } as typeof Foo; // 复制原类的静态成员到新构造函数 Object.assign(WrappedFoo, FooClass); return WrappedFoo; } @Bar class Foo { a = 1; static b = 2; constructor(args: any) { console.log('in Foo constructor') console.log('Foo instance a: ', this.a); // 现在可以正常通过Foo.b访问了 console.log('Foo static b: ', Foo.b); } } new Foo(123);
额外提示
原代码里还有个小问题:装饰器返回的函数没有返回创建的foo实例,所以new Foo(123)得到的结果是undefined。上面的解决方案里已经补上了return new FooClass(args),这样实例才能正常被返回和使用。
内容的提问来源于stack exchange,提问作者user2331095
相关产品推荐
相关产品推荐

