[英]Type-safe mixin decorator in TypeScript
I tried to define type-safe mixin()
decorator function like follows, 我试着定义类型安全的
mixin()
装饰器函数,如下所示,
type Constructor<T> = new(...args: any[]) => T;
function mixin<T>(MixIn: Constructor<T>) {
return function decorator<U>(Base: Constructor<U>) : Constructor<T & U> {
Object.getOwnPropertyNames(MixIn.prototype).forEach(name => {
Base.prototype[name] = MixIn.prototype[name];
});
return Base as Constructor<T & U>;
}
}
And used it as follows, 并使用如下,
class MixInClass {
mixinMethod() {console.log('mixin method is called')}
}
/**
* apply mixin(MixInClass) implicitly (use decorator syntax)
*/
@mixin(MixInClass)
class Base1 {
baseMethod1() { }
}
const m1 = new Base1();
m1.baseMethod1();
m1.mixinMethod(); // error TS2339: Property 'mixinMethod' does not exist on type 'Base1'.
Then, compiler said m1
didn't have the member 'mixinMethod'
. 然后,编译器说
m1
没有成员'mixinMethod'
。
And generated code is as follows, 生成的代码如下,
//...
var Base1 = /** @class */ (function () {
function Base1() {
}
Base1.prototype.baseMethod1 = function () { };
Base1 = __decorate([
mixin(MixInClass)
], Base1);
return Base1;
}());
//...
It looks that mixin
decorator was applied correctly. 看起来
mixin
decorator被正确应用了。
So, in my understanding, the type of m1
is inferred as Base1 & MixIn
. 因此,根据我的理解,
m1
的类型被推断为Base1 & MixIn
。 But compiler says it's just Base1
. 但是编译器说它只是
Base1
。
I used tsc 2.6.2
and compiled these codes with --experimentalDecorators
flag. 我使用了
tsc 2.6.2
并使用--experimentalDecorators
标志编译了这些代码。
Why does compiler fail to recognize the type as I expected? 为什么编译器无法像我预期的那样识别类型?
Based on @jcalz's answer, I modified my code as follows, 基于@jcalz的回答,我修改了我的代码如下,
type Constructor<T> = new(...args: any[]) => T
function mixin<T1, T2>(MixIns: [Constructor<T1>, Constructor<T2>]): Constructor<T1&T2>;
function mixin(MixIns) {
class Class{ };
for (const MixIn of MixIns) {
Object.getOwnPropertyNames(MixIn.prototype).forEach(name => {
Class.prototype[name] = MixIn.prototype[name];
});
}
return Class;
}
class MixInClass1 {
mixinMethod1() {}
}
class MixInClass2 {
mixinMethod2() {}
}
class Base extends mixin([MixInClass1, MixInClass2]) {
baseMethod() { }
}
const x = new Base();
x.baseMethod(); // OK
x.mixinMethod1(); // OK
x.mixinMethod2(); // OK
x.mixinMethod3(); // Property 'mixinMethod3' does not exist on type 'Base' (Expected behavior, Type check works correctly)
This works pretty well. 这非常有效。 I want to improve this
mixin
function for variable length mixin classes. 我想为可变长度的mixin类改进这个
mixin
函数。
One solution is adding overload function declaration like follows, 一种解决方案是添加如下的重载函数声明,
function mixin<T1>(MixIns: [Constructor<T1>]): Constructor<T1>;
function mixin<T1, T2>(MixIns: [Constructor<T1>, Constructor<T2>]): Constructor<T1&T2>;
function mixin<T1, T2, T3>(MixIns: [Constructor<T1>, Constructor<T2>, Constructor<T3>]): Constructor<T1&T2&T3>;
But this is too ugly. 但这太难看了。 Are there any good ideas?
有什么好主意吗? Is it impossible until variadic-kind is supported?
在支持变量类型之前是不可能的?
Decorators don't mutate the type signature of the decorated class the way you're expecting. 装饰器不会以您期望的方式改变装饰类的类型签名。 There's a rather lengthy issue in Github which discusses this, and it's not clear there's agreement on how (or if) such mutation should be implemented.
在Github中有一个相当冗长的问题讨论了这个问题,并且不清楚是否应该如何(或者是否)应该实现这种突变。 The main problem right now is that the compiler understands
Base1
as the undecorated class, and doesn't have a name for the decorated version. 现在的主要问题是编译器将
Base1
理解为未修饰的类,并且没有装饰版本的名称。
From reading that Github issue, it looks like the suggested workaround (for now at least) is something like: 从阅读Github问题看起来,看起来建议的解决方法(至少现在)是这样的:
class Base1 extends mixin(MixInClass)(
class {
baseMethod1() { }
}) {
}
So you're not using the decorator @
notation, and instead directly applying the decorator function to an anonymous class (which has the same implementation of your desired Base1
), and then subclassing that to get Base1
. 所以你不使用装饰
@
符号,和而不直接应用的装饰功能,一个匿名类(具有相同的实现所需的Base1
),然后继承,为了获得Base1
。 Now the compiler understands that Base1
has both a baseMethod1()
and a mixinMethod()
. 现在编译器知道
Base1
同时具有baseMethod1()
和mixinMethod()
。
Hope you find that helpful. 希望你觉得有帮助。 Good luck!
祝好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.