![](/img/trans.png)
[英]How do I link/join/relate the types of two class fields in TypeScript?
[英]How do I link/join/relate the types of two function parameters in TypeScript?
基於關於鏈接類字段的類似問題,我如何讓 TypeScript 識別函數的一個參數的值(其值在運行時確定)限制了另一個參數的類型?
示例代碼( 操場):
class Cat { purr() {/*...*/} }
class Dog { bark() {/*...*/} }
interface TypeMap {
cat: Cat;
dog: Dog;
}
const makeSound = function<C extends keyof TypeMap>(
commonName: C,
animal: TypeMap[C],
) {
if(commonName === 'cat') {
//Error: this.animal is of type 'Cat | Dog',
//not narrowed to Cat as hoped.
return animal.purr();
} else if (commonName === 'dog') {
//Error: this.animal is of type 'Cat | Dog',
//not narrowed to Dog as hoped.
return animal.bark();
}
}
//The intended type-checking is being done in calling contexts:
const caller = function(cat: Cat, dog: Dog) {
//Error on next line as expected:
//Argument of type '"dog"' is not assignable
//to parameter of type '"cat"'.ts(2345)
makeSound<'cat'>('dog', dog);
makeSound<'cat'>('cat', dog); //similar error, as expected
makeSound<'cat'>('cat', cat); //works
makeSound('cat', cat); //desired style; cat vs. dog not hard-codable
}
TS 文檔指出“您可以聲明受另一個類型參數約束的類型參數。” 但是給出的示例非常簡單,並且在泛化到像這個稍微復雜的示例的能力方面似乎很脆弱。
您正在針對個別情況手動檢查commonName
,如果makeSound()
的主體是generic ,TypeScript 確實沒有很好的類型檢查支持。 即使commonName
是泛型類型C
,檢查commonName
只會縮小commonName
本身的類型,它不會進一步限制C
。 所以不幸的是,即使編譯器知道commonName
是"cat"
,它也不知道C
是"cat"
。 依舊可以是"cat" | "dog"
"cat" | "dog"
,因此擔心(誠然不太可能)這樣的電話的可能性
makeSound(
Math.random() < 0.99 ? "cat" : "dog",
Math.random() < 0.99 ? dog : cat
); // no error?!
我們實際上沒有建立我們想要建立的鏈接,即使它通常已經足夠好(我的意思是,真的,誰將交叉相關的聯合傳遞給這樣的函數?),編譯器不願意對主體進行類型檢查。
這里有各種要求改進的功能請求。 例如, microsoft/TypeScript#27808希望可以說C extends oneof keyof TypeMap
以便拒絕 TypeMap 的完整聯合類型keyof TypeMap
。 現在雖然什么都沒有實現,如果我們想編寫你的代碼,我們將需要重構(或者用類型斷言來填充你的代碼以抑制錯誤......但讓我們研究重構。)
因為makeSound()
的返回類型不依賴於C
,所以您可以將調用簽名重構為強制執行實際約束的非泛型版本。 本質上,參數tuple , [commonName, animal]
形成了一個可區分的聯合,其中索引0
處的屬性是判別式,檢查它會縮小索引1
處的屬性類型。 你可以像這樣計算這個聯合類型:
type MakeSoundParam = { [K in keyof TypeMap]:
[commonName: K, animal: TypeMap[K]]
}[keyof TypeMap];
/* type MakeSoundParam =
[commonName: "cat", animal: Cat] |
[commonName: "dog", animal: Dog] */
TypeScript 支持對解構的可區分聯合進行控制流分析,這意味着您可以將函數重寫為:
const makeSound = function (...[commonName, animal]: MakeSoundParam) {
if (commonName === 'cat') {
return animal.purr(); // okay
} else if (commonName === 'dog') {
return animal.bark(); // okay
}
}
編譯沒有錯誤。 並且只是為了確保makeSound()
從調用方的角度仍然正常運行:
const caller = function (cat: Cat, dog: Dog) {
makeSound('cat', cat); // ok
makeSound('dog', dog); // ok
makeSound('cat', dog); // error
makeSound('dog', cat); // error
makeSound(
Math.random() < 0.99 ? "cat" : "dog",
Math.random() < 0.99 ? dog : cat
); // error
}
看起來不錯,連互相關調用都被拒絕了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.