我正在使用猫鼬并遇到一个非常令人讨厌的打字稿问题,我不明白。我已经解决了问题并简化了它。
假设函数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
属性。
要完成此示例,Document
和Data
接口看起来像
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
来自猫鼬,但Collection
是我的。receiveMessage
。任何建议问题可能是什么?
答案 0 :(得分:2)
T
班级中的Collection<T extends Data>
与T
界面中的Model<T>()
不一样。
问题与接口本身的细节无关 - 问题在于,为了能够拥有Model<TDocument>
类型的对象,TDocument
必须实现Document
接口。您对T
类中的Collection
没有这样的约束(相反,它仅限于支持Data
接口。)
您可以执行以下操作,以确保您的集合中的T
仅限于 Data
和Document
接口的
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'.
类型检查器的错误跟踪非常清楚。如您所见,它试图断言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
和{{ 1}}方法,它满足created
中的类型约束。使用结构类型系统,我们不需要明确使用model<T extends Document>()
,只需具有相同的属性即可。