![](/img/trans.png)
[英]How do I link/join/relate the types of two function parameters in 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 。
Pet
的commonName
和animal
屬性自然使Pet
成為可區分的聯合,其中commonName
屬性是判別式。 如果Pet
是這樣一個有區別的聯合,您的makeSound()
實現將毫無問題地進行類型檢查。
唯一的問題是Pet
是一個class
,類實例類型需要定義為interface
,接口根本不能是聯合,更不用說可區分聯合了。
相反,您試圖將Pet
表示為generic 。 這或多或少地捕獲了commonName
和animal
的約束(好吧, 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了解更多信息)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.