打字稿参考扩展类

时间:2018-08-18 22:37:22

标签: typescript class

我需要引用一个扩展了我的基类的类,而在弄清楚它时遇到了很多麻烦。我不确定如何解释它,所以我做了一个演示代码:

interface BaseAttributes {
    id: string;
}

interface Relation {
    field: string;
    class: typeof One;
}

    class One<A extends BaseAttributes> {
    private relations: Relation[];
    private attr: A;

    constructor(attr: A) {
        this.attr = attr;

        this.relations = (this.constructor as typeof One).getRelations();
    }

    static getRelations(): Relation[] {
        return [];
    }

    static getOne() {
        // Some data fetching here
        const data: TwoAttributes = {
            id: '123',
            label: 'test',
        };

        return new this(data);
    }

    doRelations() {
        return this.relations.map((relation: Relation) => {
            return relation.class.getOne();
        });
    }
}

interface TwoAttributes extends BaseAttributes{
    label: string;
}

class Two extends One<TwoAttributes> {
    static getRelations(): Relation[] {
        return [
            {
                field: 'test',
                class: Three,
            },
        ];
    }
}

interface ThreeAttributes extends BaseAttributes{
    image: string;
}

class Three extends One<ThreeAttributes> {
}

基本上,“一个”是一类,它具有一些属性和关系。这些关系可以引用其他“一个”类或扩展它的类。问题是,在“ Two”类中-getRelations的实现-我得到了

Type '{ field: string; class: typeof Three; }[]' is not assignable to type 'Relation[]'.
  Type '{ field: string; class: typeof Three; }' is not assignable to type 'Relation'.
    Types of property 'class' are incompatible.
      Type 'typeof Three' is not assignable to type 'typeof One'.
        Types of parameters 'attr' and 'attr' are incompatible.
          Type 'A' is not assignable to type 'ThreeAttributes'.
            Type 'BaseAttributes' is not assignable to type 'ThreeAttributes'.
              Property 'image' is missing in type 'BaseAttributes'.

我尝试弄乱使用'typeof'而不使用它的各种变化,但是在Relation中,我需要它来引用类本身。

我也能够找到同样问题的问题,但从未得到回答。 Link to the SO question

2 个答案:

答案 0 :(得分:1)

由于值One是泛型类的构造函数,因此其类型typeof One类似于:

type TypeofOne = {
    new <A extends BaseAttributes>(attr: A): One<A>;
    getRelations(): Relation[];
}

(您可以通过注意以下代码没有错误来验证这些类型基本相同:

type Same<T extends U, U extends V, V = T> = true;
declare const checkTypeofOne: Same<typeof One, TypeofOne>;

在这种情况下,编译器会看到typeof OneTypeofOne相互扩展……这几乎是“同一类型”。)

无论如何,该签名意味着typeof one是一个构造函数,该构造函数采用通用类型A的参数并返回One<A>。但是Three不会 那样做。它仅接受类型为ThreeAttributes的参数,并产生一个One<ThreeAttributes>。因此,您无法在期望Three的地方传递TypeofOne

因此,也许您并不是真的希望将class的{​​{1}}属性限制为Relation


也许您真的想说些类似的话:“ TypeofOne的{​​{1}}属性可以是为某些对象生成class实例的任何构造函数。 Relation”。从技术上讲,这需要existential types进行正确编码,并且需要TypeScript doesn't have those。但也许您会满意:“ One<A>的{​​{1}}属性是一个生成A的构造函数”。像这样:

class

在这种情况下,您可以看到Relation不一定是通用构造函数。它将匹配一个采用One<any>参数并生成type TypeofAnyOne = { new(attr: any): One<any>; getRelations(): Relation[]; } 的构造函数。警告:每次使用the any type时,您都不完全是安全类型...在这种情况下,TypeofAnyOne可能会匹配接受any并生成{{1 }},因为One<any>TypeofAnyOne都不匹配string,这甚至是不可能的。

因此,如果将One<number>更改为string而不是number,它将正常工作:

BaseAttributes

这可能对您来说已经足够了,这取决于您实际上想对Relation['class']数组执行的操作。无论如何,希望能帮助您找到有用的方向。祝你好运!

答案 1 :(得分:0)

即使接受的答案提供了解决方案,我仍然可以通过另一种方法解决该问题,该方法摆脱了代码重复,但引入了另一种丑陋的事物。

代替使用

static getRelations(): Relation[] {
    return [
        {
            field: 'test',
            class: Three,
        },
    ];
}

我能够使它像这样工作:

static getRelations(): Relation[] {
    return [
        {
            field: 'test',
            class: Three as typeof One,
        },
    ];
}

这很好,因为它允许我使用

interface Relation {
    field: string;
    class: typeof One;
}

,并仍然获得One定义的所有方法。 as仍然会产生奇怪的代码,尤其是在存在更多关系的情况下,但总的来说,我更喜欢它。