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

Passing generic types through to mixins in Typescript

我正在尝试在 Typescript 中创建通用类型的 mixins。我意识到,根据 issue #13979,从开发版本 2.8.0 开始,Typescript 还不直接支持这一点。我正在寻找解决方法。我不在乎 mixins 本身有多难看,只要应用 mixins 干净且可读即可。

我需要一些混音中的装饰器支持,所以我将混音定义为嵌套的 classes,仿照 . The more common approach returns class expressions instead。我还没有注意到在嵌套-class 方法中丢失了任何功能。

我特别想要在外部混入中指定的泛型类型,以确定在更多嵌套混入中找到的类型,至少从外部混入外部来看是这样。我意识到可能需要一种更加截然不同的方法来处理 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";
}

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

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

我也很乐意这样做,但这也行不通:

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

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

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

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

要启用选项 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);
        }
    }
}

使用两次调用方法可以实现 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
}