簡體   English   中英

打字稿:嘗試在界面中使用`this`在泛型中使用`extends`

[英]Typescript: trying to use `extends` in generics with `this` in the interface

我正在使用貓鼬,並遇到了一個非常惱人的打字稿問題,我不明白。 我已經解決了問題並簡化了它。

假設函數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;
}

這個問題在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引起的increment(): this或只是this 如果刪除該行或替換thisDocument的問題已經一去不復返了。

在最初的問題中, DocumentmodelModel來自mongoose,但Collection是我的。 任何建議問題可能是什么?

TCollection<T extends Data>類是不一樣的TModel<T>()接口。

問題與接口本身的細節無關 - 問題是,為了能夠擁有Model<TDocument>類型的對象, TDocument必須實現Document接口。 您對Collection類中的T沒有這樣的約束(相反,它僅限於實現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>;
}

如果您按如下所示重寫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 extends Data & Document意味着T應該具有DataDocument的屬性,即incrementcreated方法,它滿足model<T extends Document>()的類型約束。 對於結構類型系統,我們不需要顯式使用extends ,只需具有相同的屬性就足夠了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM