TypeScript中的类型安全的mixin装饰器 [英] Type-safe mixin decorator in TypeScript

查看:442
本文介绍了TypeScript中的类型安全的mixin装饰器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图定义类型安全的 mixin()装饰器函数,如下所示,

I tried to define type-safe mixin() decorator function like follows,

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>;
    }
}

并按如下方式使用它,

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'.

然后,编译器说 m1 没有具有成员'mixinMethod'

Then, compiler said m1 didn't have the member 'mixinMethod'.

生成的代码如下,

//...
var Base1 = /** @class */ (function () {
    function Base1() {
    }
    Base1.prototype.baseMethod1 = function () { };
    Base1 = __decorate([
        mixin(MixInClass)
    ], Base1);
    return Base1;
}());
//...

看起来是 mixin 装饰器已正确应用。

It looks that mixin decorator was applied correctly.

因此,据我了解,推断出 m1 的类型为 Base1& MixIn 。但是编译器说它只是 Base1

So, in my understanding, the type of m1 is inferred as Base1 & MixIn. But compiler says it's just Base1.

我用的是 tsc 2.6.2 并使用-experimentalDecorators 标志编译了这些代码。

I used tsc 2.6.2 and compiled these codes with --experimentalDecorators flag.

为什么编译器无法识别类型如我所料?

Why does compiler fail to recognize the type as I expected?

根据@jcalz的回答,我将代码修改如下,

Based on @jcalz's answer, I modified my code as follows,

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)

这很好用。我想为可变长度混合类改进此 mixin 函数。

This works pretty well. I want to improve this mixin function for variable length mixin classes.

一种解决方案是添加如下所示的重载函数声明,

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>;

但这太丑了。有什么好主意吗?在支持 variadic-kind 之前是否不可能?

But this is too ugly. Are there any good ideas? Is it impossible until variadic-kind is supported?

推荐答案

装饰器不会按照您期望的方式来修饰装饰类的类型签名。 Github中有一个相当长的问题对此进行了讨论,目前尚不清楚如何达成共识(或是否)应实施此类突变。现在的主要问题是编译器将 Base1 理解为未修饰的类,并且没有修饰版本的名称。

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. 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.

从阅读Github问题开始,看来建议的解决方法(至少目前如此)类似于:

From reading that Github issue, it looks like the suggested workaround (for now at least) is something like:

class Base1 extends mixin(MixInClass)(
  class {
    baseMethod1() { }
  }) {
}

所以您没有使用装饰器 @ 表示法,而是直接将装饰器功能应用于匿名类(具有与所需的 Base1 相同的实现),然后将 that 子类化以获得 Base1 。现在,编译器了解到 Base1 同时具有 baseMethod1() mixinMethod()

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. Now the compiler understands that Base1 has both a baseMethod1() and a mixinMethod().

希望您会有所帮助。祝你好运!

Hope you find that helpful. Good luck!

这篇关于TypeScript中的类型安全的mixin装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆