将泛型类型传递给 Typescript 中的 mixins

Joe*_*app 2 generics mixins typescript

我正在尝试在 Typescript 中制作通用类型的 mixin。我意识到从开发版本 2.8.0 开始,Typescript 还没有直接支持这个,每个问题 #13979。我正在寻找解决方法。我不在乎 mixin 本身有多丑,只要应用 mixin 是干净和可读的。

我需要一些 mixin 中的装饰器支持,所以我将 mixin 定义为嵌套类,以这个有用的 SO answer为模型。更常见的方法是返回类表达式。我还没有注意到在嵌套类方法中丢失了任何功能。

我特别想要一个在外部 mixin 中指定的泛型类型来确定在更多嵌套 mixin 中找到的类型,至少在外部 mixin 之外查看。我意识到可能需要一种更彻底不同的方法来处理 Typescript 可能无法通过嵌套函数调用向下传递外部函数中定义的类型的可能性。

鉴于以下代码:

interface ConcreteClass<C> { new (...args: any[]): C; }

class Entity {
    name = "base";
}

class Broker<E extends Entity> {
    static table = "entities";
}

interface BrokerType<E extends Entity> extends ConcreteClass<Broker<E>> {
    table: string;
}

class IdEntity extends Entity {
    id = 1;
}

function IdMixin<E extends IdEntity, B extends BrokerType<E>>(Base: B) {
    class AsIdBroker extends Base {
        constructor(...args: any[]) {
            super(...args);
        }
        getEntity(id: number): E | null {
            return null;
        }
    }
    return AsIdBroker;
}

class TaggedEntity extends IdEntity {
    tag = "gotcha";
}
Run Code Online (Sandbox Code Playgroud)

这是执行此操作的理想方法,但这不起作用:

// OPTION A -- broken

class TaggedBroker extends Broker<TaggedEntity> { };

class MyTaggedBroker extends IdMixin(TaggedBroker) {
    myStuff = "piled";
    showTag(id: number) {
        const entity = this.getEntity(id);
        if (entity) {
            // ERROR: Property 'tag' does not exist on type 'IdEntity'.
            console.log(entity.tag);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我也很乐意这样做,但这也不起作用:

// OPTION B -- broken

class TaggedBroker extends Broker<TaggedEntity> { };

// ERROR: Expected 2 type arguments, but got 1.
class MyTaggedBroker extends IdMixin<TaggedEntity>(TaggedBroker) {
    myStuff = "all mine";
    showTag(id: number) {
        const entity = this.getEntity(id);
        if (entity) {
            console.log(entity.tag);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一种方法使所有现有代码都可以工作,但除了冗长(至少在我的应用程序中使用名称)之外,它不会继承基本代理的类型,因此不是真正的混合:

// OPTION C -- works here, but drops types for any previously mixed-in brokers

class TaggedBroker extends Broker<TaggedEntity> { };

class MyTaggedBroker extends IdMixin<TaggedEntity, BrokerType<TaggedEntity>>(TaggedBroker) {
    myStuff = "all mine";
    showTag(id: number) {
        const entity = this.getEntity(id);
        if (entity) {
            console.log(entity.tag);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里发帖以防有人知道我需要做什么。同时,我将继续探索我的选择,包括可能的 IoC 方法首先输入嵌套最多的 mixin。

Tit*_*mir 5

要启用选项 C 以将成员表单继承TaggedBrokerMyTaggedBroker您,只需进行一个简单的更改:

class TaggedBroker extends Broker<TaggedEntity> { 
    public x: number;
};

class MyTaggedBroker extends IdMixin<TaggedEntity, typeof TaggedBroker>(TaggedBroker) {
    myStuff = "all mine";
    showTag(id: number) {
        console.log(this.x);
        const entity = this.getEntity(id);
        if (entity) {
            console.log(entity.tag);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

IdMixin可以使用两次调用方法实现更简洁的用法,其中E在第一次调用中明确指定,并B从第二次调用的参数值推断:

function IdMixin<E extends IdEntity>()  {
    return function <B extends BrokerType<E>>(Base: B){
        class AsIdBroker extends Base {
            constructor(...args: any[]) {
                super(...args);
            }
            getEntity(id: number): E | null {
                return null;
            }
        }
        return AsIdBroker;
    }
}
//Usage
class MyTaggedBroker extends IdMixin<TaggedEntity>()(TaggedBroker) {
    // Same as before
}
Run Code Online (Sandbox Code Playgroud)

  • 天哪,你是个魔术师!这两种方法确实使这个例子有效。现在将它合并到我的图书馆中,看看我是否遗漏了什么。非常感谢!一会儿回来... (2认同)