[英]How does type inference work with union types (+conditional types and generics) in TypeScript?
我正在用 Angular 開發一個游戲,並試圖將演示與游戲邏輯分離。 為了實現這一點,我構建了一個單獨的UiController
服務來處理用戶交互和演示。 每當需要顯示某些內容或需要用戶操作時,與游戲邏輯相關的服務都會向UiController
發出請求。
為了盡可能巧妙地實現這一點,我試圖抽象出接口以與UiController
交互。 一種常見的交互是選擇,當玩家必須在同一類別的不同選項中選擇一個時使用。 該交互由UiController
的requestChoice()
方法處理,該方法需要ChoiceRequest
類型的參數。 由於有許多不同的類別可供選擇,因此該類型必須包含所有這些類別,並且該方法必須知道如何處理所有這些類別。
例如,用戶可能需要選擇怪物或英雄。 我使用文字類型來指代選擇中的選項:
type HeroType = 'warrior' | 'rogue' | 'mage';
type MonsterType = 'goblin' | 'demon' | 'dragon';
我ChoiceRequest
的第一種構建ChoiceRequest
是使用泛型和條件類型:
type ChoiceType = 'hero' | 'monster';
type OptionsSet<T extends ChoiceType> = T extends 'hero'
? HeroType[]
: T extends 'monster'
? MonsterType[]
: never;
interface ChoiceRequest<T extends ChoiceType> {
player: Player;
type: T;
options: OptionsSet<T>;
}
這在構建這樣的選擇請求時被證明是有用的,因為options
中type
和項目的值被正確預測或拒絕:
const request: ChoiceRequest<'monster'> = {
player: player2,
type: 'monster', // OK, any other value wrong
options: ['demon', 'goblin'] // OK, any value not included in MonsterType wrong.
}
但是,當我嘗試使requestChoice()
方法處理不同情況時,類型推斷無法按預期工作:
public requestChoice<T extends ChoiceType>(request: ChoiceRequest<T>) {
switch (request.type) {
case 'a': // OK, but should complain since values can only be 'hero' or 'monster'
...
case 1: // Here it complains, see below (*)
...
...
}
}
(*) 類型 'number' 不能與類型 'T' 進行比較。 'T' 可以用與 'number' 無關的任意類型實例化。
我以前反復遇到過這個問題,但我不完全明白為什么會發生這種情況。 我認為它與條件類型有關,所以我嘗試了一種不太優雅的第二種方法:
interface ChoiceMap {
hero: HeroType[];
monster: MonsterType[];
}
type ChoiceType = keyof ChoiceMap;
interface ChoiceRequest<T extends ChoiceType> {
player: Player;
type: T;
options: ChoiceMap[T];
}
但是,這種方法與第一種方法完全一樣。
使這項工作按預期進行的唯一方法是第三種方法,將ChoiceRequest
顯式構建為標記聯合,沒有泛型或條件類型:
interface MonsterRequest {
player: Player;
type: 'monster';
options: MonsterType[];
}
interface HeroRequest {
player: Player;
type: 'hero';
options: HeroType[];
}
type ChoiceRequest = MonsterRequest | HeroRequest;
問題:為什么第三種方法有效而前兩種方法無效? 關於類型推斷的工作原理,我缺少什么? 在這樣的場景中,是否還有其他模式可以實現我所需要的?
如果您不需要返回類型中的 T 一個非常簡單的解決方法可能是:
function requestChoice(request: ChoiceRequest<ChoiceType>) {
switch (request.type) {
case 'a': // Type '"a"' is not comparable to type ChoiceType
case 1: // Type '1' is not comparable to type ChoiceType
case "hero": // fine
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.