typescript :尝试在接口(interface)中使用 `extends` 的泛型中使用 `this`

标签 typescript generics

我在使用 mongoose 时遇到了一个我不明白的非常烦人的 typescript 问题。我已经提取了问题并对其进行了简化。

假设函数 model 创建了 Model

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)看起来像

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(): this 行或 this 行引起的。如果您删除该行或将 this 替换为 Document,问题就消失了。

原题中,DocumentmodelModel来自mongoose,Collection是我的。任何建议可能是什么问题?

最佳答案

如果重写 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>

我们不需要 Data 之间的继承和 Document不再使用此解决方案。由于 TypeScript 具有结构类型系统,因此新定义了 T extends Data & Document意味着 T应该同时具有 Data 的属性和 Document ,即 incrementcreated方法,它满足 model<T extends Document>() 中的类型约束.使用结构类型系统,我们不需要显式使用 extends , 具有相同的属性就足够了。

关于 typescript :尝试在接口(interface)中使用 `extends` 的泛型中使用 `this`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48927979/

相关文章:

reactjs - 在 ReactJS 中使用复杂对象的 useState 无法按预期工作

TypeScript v2.3.1 破坏了单元测试

java - 不同的嵌套列表,按值属性比较列表项,Java8 Lambda

java - 如何在 Java 泛型中返回类对象 "type"?

typescript - 如何在 typescript 中创建静态 map ?

javascript - 语言不可知-函数和构造函数参数中的允许类型

angular - 将 ionic3 转换为 apk 构建

java - 我可以有一个包含 A 或 List<A> 的类型安全映射吗?

Java泛型列表参数不可能?

java - 具有可变类型参数的泛型