打字稿:试图在界面

时间:2018-02-22 12:54:46

标签: typescript generics

我正在使用猫鼬并遇到一个非常令人讨厌的打字稿问题,我不明白。我已经解决了问题并简化了它。

假设函数model创建Model s

interface Model<T extends Document> {}

function model<T extends Document>(): Model<T> {
    return {} as Model<T>;
}

我在class中使用此功能,如下所示:

class Collection<T extends Data> {
    private Model: Model<T>;

    constructor() {
        this.Model = model<T>();
    }

    isRecordValid(record: T): boolean {
        return record.created < new Date();
    }
}

它是T extends Data,因为我需要访问created属性。 要完成此示例,DocumentData接口看起来像

interface Document {
    increment(): this;
}

interface Data extends Document {
    created: Date;
}

Code

问题在Webstorm中已经可见(红色摆动下划线):

enter image description here

现在,当我编译这段代码时

$> tsc test.ts

我收到以下错误:

test.ts(39,28): error TS2344: Type 'T' does not satisfy the constraint 'Document'.
  Type 'Data' is not assignable to type 'Document'.
    Types of property 'increment' are incompatible.
      Type '() => Data' is not assignable to type '() => T'.
        Type 'Data' is not assignable to type 'T'.

错误是由行increment(): thisthis引起的。如果删除该行或将this替换为Document,问题就会消失。

在原始问题中,DocumentmodelModel来自猫鼬,但Collection是我的。receiveMessage。任何建议问题可能是什么?

2 个答案:

答案 0 :(得分:2)

T班级中的Collection<T extends Data>T界面中的Model<T>()不一样。

问题与接口本身的细节无关 - 问题在于,为了能够拥有Model<TDocument>类型的对象,TDocument必须实现Document接口。您对T类中的Collection没有这样的约束(相反,它仅限于支持Data接口。)

您可以执行以下操作,以确保您的集合中的T仅限于 DataDocument接口的

interface Model<T extends Document> {}

function model<T extends Document>(): Model<T> {
    return {} as Model<T>;
}

interface DataAndDoucment extends Data, Document {

}

class Collection<T extends DataAndDoucment> {
  private Model: Model<T>;
}

答案 1 :(得分:2)

如果您按如下方式重写Document类,这相当于返回类型this,则错误消息会变得更加清晰。

interface Document<U extends Document<U>> {
    increment(): U;
}

interface Data extends Document<Data> {
    created: Date;
}

interface Model<T extends Document<T>> {}

function model<T extends Document<T>>(): Model<T> {
    return {} as Model<T>;
}

class Collection<T extends Data> {

    private Model: Model<T>;

    constructor() {
        this.Model = model<T>();
    }

    isRecordValid(record: T): boolean {
        return record.created < new Date();
    }
}

现在,在Model<T>的两个定义中,您将收到错误:

Type 'T' does not satisfy the constraint 'Document<T>'.
  Type 'Data' is not assignable to type 'Document<T>'.
    Types of property 'increment' are incompatible.
      Type '() => Data' is not assignable to type '() => T'.
        Type 'Data' is not assignable to type 'T'.

类型检查器的错误跟踪非常清楚。如您所见,它试图断言DataDocument<T>的子类型,为此,它要求DataT的子类型。

解决这个问题的一种方法是使Data通用,如下所示:

interface Data<T extends Data<T>> extends Document<T> {
    created: Date;
}

然后将集合类定义为

class Collection<T extends Data<T>> {
   ...
}

<强>更新

我忽略了Document类型无法更改的事实。正如Jeanluca本人的评论中所建议的那样,我们可以通过使用交集类型来解决这个问题:

interface Data {
    created: Date;
}

class Collection<T extends Data & Document>

此解决方案不再需要DataDocument之间的继承。由于TypeScript具有结构化类型系统,因此T extends Data & Document的新定义意味着T应具有DataDocument的属性,即increment和{{ 1}}方法,它满足created中的类型约束。使用结构类型系统,我们不需要明确使用model<T extends Document>(),只需具有相同的属性即可。