簡體   English   中英

如何在 TypeScript 中鏈接/加入/關聯兩個類字段的類型?

[英]How do I link/join/relate the types of two class fields in TypeScript?

如何讓 TypeScript 識別類中一個字段的值限制了另一個字段的類型?

示例代碼( 操場):

class Cat { purr() {/*...*/} }
class Dog { bark() {/*...*/} }
interface TypeMap {
    cat: Cat;
    dog: Dog;
}
class Pet<C extends keyof TypeMap> {
    commonName: C;
    animal: TypeMap[C];
    constructor(commonName : C, animal: TypeMap[C]) {
        this.commonName = commonName;
        /*
        //Dropping the parameter and trying to create the object here doesn't work:
        if(commonName === 'cat') {
            this.animal = new Cat();
        } //...
        // because Type 'Cat' is not assignable to type 'Cat & Dog'.
        */
        this.animal = animal;
    }
    makeSound() {
        if(this.commonName === 'cat') {
            //Error: this.animal is of type 'Cat | Dog',
            //not narrowed to Cat as hoped.
            return this.animal.purr();
        } else if (this.commonName === 'dog') {
            //Error: this.animal is of type 'Cat | Dog',
            //not narrowed to Dog as hoped.
            return this.animal.bark();
        }
    }
}

makeSound()中顯示的限制類型是我正在嘗試完成的一個示例 - 您應該能夠檢查 commonName 以縮小其類型的方式更多地了解this.animal的類型。

關於函數參數的相關問題是here

PetcommonNameanimal屬性自然使Pet成為可區分的聯合,其中commonName屬性是判別式 如果Pet是這樣一個有區別的聯合,您的makeSound()實現將毫無問題地進行類型檢查。

唯一的問題是Pet一個class ,類實例類型需要定義為interface ,接口根本不能是聯合,更不用說可區分聯合了。

相反,您試圖將Pet表示為generic 這或多或少地捕獲了commonNameanimal的約束(好吧, C本身可以是一個聯合類型,這會破壞一些東西,但是對於這種情況,我們將忽略潛在的不健全。為了我們的目的,我們可以假設C將可以是"cat""dog"keyof TypeMap的任何其他單個成員)。 但是編譯器不允許makeSound()進行類型檢查。

所以我們將不得不重構(或者使用類型斷言來抑制錯誤,但我們不要這樣做)。


一種潛在的重構是將makeSound()中的檢查抽象為查找,以便我們不進行逐個案例的控制流。 它看起來像這樣:

const petSound = {
    cat: (cat: Cat) => cat.purr(),
    dog: (dog: Dog) => dog.bark()
}

class Pet<C extends keyof TypeMap> {
    makeSound() {
        petSound[this.commonName](this.animal); // error
    }
}

問題是編譯器無法跟蹤petSound[this.commonName]this.animal類型之間的相關性。 這就是我一直稱之為“相關聯合”的問題,如microsoft/TypeScript#30581中所述。 對此的修復已在microsoft/TypeScript#47109中實現,並涉及給petSound一個分布式對象類型,編譯器可以在其中遵循相關性。 它看起來像這樣:

const petSound: { [C in keyof TypeMap]: (animal: TypeMap[C]) => void } =
{
    cat: cat => cat.purr(),
    dog: dog => dog.bark()
};

僅僅通過給petSound一個迭代keyof TypeMap TypeMap 的映射類型,上面的makeSound()編譯沒有錯誤:

class Pet<C extends keyof TypeMap> {
    makeSound() {
        petSound[this.commonName](this.animal); // okay
    }
}

看起來不錯!

(請注意,這仍然是不合理的;有人可以將C指定為keyof TypeMap ,然后為給定的commonName屬性提供完全錯誤的animal屬性。但是 TypeScript 故意忽略這種不合理性,以便使這個相關的聯合事物起作用。請參閱microsoft/TypeScript# 48730了解更多信息)。

Playground 代碼鏈接

暫無
暫無

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

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