编写 TypeScript 装饰器以始终将 class 方法绑定到 'this'
Writing a TypeScript decorator to always bind class methods to 'this'
我正在尝试编写一个装饰器以始终将 class 方法的范围绑定到该 class 的实例。
这是我目前的实现:
function LockThis<T extends { new(...args: any[]): {} }>(constructor: T) {
let self: any;
const locker = class extends constructor {
constructor(...args: any[]) {
super(...args);
self = this;
}
};
const proto = constructor.prototype;
Object.getOwnPropertyNames(proto).forEach(key => {
if (key === 'constructor') {
return;
}
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
if (descriptor && typeof descriptor.value === 'function') {
const original = descriptor.value;
locker.prototype[key] = (...a: any[]) => original.apply(self, a);
}
});
return locker;
}
@LockThis
class Something {
private foo = 'bar';
public doIt(someVar?: string) {
return this.foo + ' ' + someVar;
}
}
const something = new Something();
console.log(something.doIt.call({}, 'test'));
--> bar test
这有效,除了摘要 classes:
@LockThis
abstract class Blah {
}
TS2345: Argument of type 'typeof Blah' is not assignable to parameter of type 'new (...args: any[]) => {}'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
是否可以使用不同的类型保护来允许实际 classes 和抽象 classes and/or 一种方法来代替每个方法执行此操作?
(我对每个方法的尝试都是徒劳的,因为在调用该方法之前我似乎无法确定 'this',如果使用不同的范围调用则为时已晚)
class Something {
private foo = 'bar';
@LockThis()
public doIt(someVar?: string) {
return this.stuff + ' ' + someVar;
}
}
根据 Microsoft/TypeScript#5843,没有很好的方法来引用抽象构造函数类型。那里提到的解决方法是只使用 Function
,这太宽松了(并非所有函数都是构造函数),但可能对你有用,因为你不太可能尝试在上使用 class 装饰器一个随机函数。
而且你不能在 Function
上进行混合,所以实现仍然需要认为你有一个构造函数。因此,我建议您在 LockThis
上使用 function overload,以便调用者看到 Function
,但实现仍然看到它可以扩展的构造函数。例如:
// callers see a function that takes any function and returns the same type
function LockThis<T extends Function>(constructor: T): T;
// implementation is unchanged, and still sees a (concrete) constructor
function LockThis<T extends { new(...args: any[]): {} }>(constructor: T) {
// ... your unchanged implementation here
}
现在可以使用了:
@LockThis // no error
abstract class Blah { }
这是我能得到的最接近你想要的。希望有所帮助;祝你好运!
我正在尝试编写一个装饰器以始终将 class 方法的范围绑定到该 class 的实例。
这是我目前的实现:
function LockThis<T extends { new(...args: any[]): {} }>(constructor: T) {
let self: any;
const locker = class extends constructor {
constructor(...args: any[]) {
super(...args);
self = this;
}
};
const proto = constructor.prototype;
Object.getOwnPropertyNames(proto).forEach(key => {
if (key === 'constructor') {
return;
}
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
if (descriptor && typeof descriptor.value === 'function') {
const original = descriptor.value;
locker.prototype[key] = (...a: any[]) => original.apply(self, a);
}
});
return locker;
}
@LockThis
class Something {
private foo = 'bar';
public doIt(someVar?: string) {
return this.foo + ' ' + someVar;
}
}
const something = new Something();
console.log(something.doIt.call({}, 'test'));
--> bar test
这有效,除了摘要 classes:
@LockThis
abstract class Blah {
}
TS2345: Argument of type 'typeof Blah' is not assignable to parameter of type 'new (...args: any[]) => {}'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
是否可以使用不同的类型保护来允许实际 classes 和抽象 classes and/or 一种方法来代替每个方法执行此操作?
(我对每个方法的尝试都是徒劳的,因为在调用该方法之前我似乎无法确定 'this',如果使用不同的范围调用则为时已晚)
class Something {
private foo = 'bar';
@LockThis()
public doIt(someVar?: string) {
return this.stuff + ' ' + someVar;
}
}
根据 Microsoft/TypeScript#5843,没有很好的方法来引用抽象构造函数类型。那里提到的解决方法是只使用 Function
,这太宽松了(并非所有函数都是构造函数),但可能对你有用,因为你不太可能尝试在上使用 class 装饰器一个随机函数。
而且你不能在 Function
上进行混合,所以实现仍然需要认为你有一个构造函数。因此,我建议您在 LockThis
上使用 function overload,以便调用者看到 Function
,但实现仍然看到它可以扩展的构造函数。例如:
// callers see a function that takes any function and returns the same type
function LockThis<T extends Function>(constructor: T): T;
// implementation is unchanged, and still sees a (concrete) constructor
function LockThis<T extends { new(...args: any[]): {} }>(constructor: T) {
// ... your unchanged implementation here
}
现在可以使用了:
@LockThis // no error
abstract class Blah { }
这是我能得到的最接近你想要的。希望有所帮助;祝你好运!