我在使用 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
属性。
为了完成示例,Document
和 Data
接口(interface)看起来像
interface Document {
increment(): this;
}
interface Data extends Document {
created: Date;
}
该问题在 Webstorm 中已经可见(红色摆动下划线):
现在,当我编译这段代码时
$> 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
,问题就消失了。
原题中,Document
、model
和Model
来自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'.
类型检查器的错误痕迹非常清楚。如您所见,它试图断言 Data
是 Document<T>
的子类型,为此,它需要 Data
是 T
的子类型.
修复它的一种方法是同时生成 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
,即 increment
和 created
方法,它满足 model<T extends Document>()
中的类型约束.使用结构类型系统,我们不需要显式使用 extends
, 具有相同的属性就足够了。
关于 typescript :尝试在接口(interface)中使用 `extends` 的泛型中使用 `this`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48927979/