簡體   English   中英

類型推斷如何與 TypeScript 中的聯合類型(+條件類型和泛型)一起使用?

[英]How does type inference work with union types (+conditional types and generics) in TypeScript?

我正在用 Angular 開發一個游戲,並試圖將演示與游戲邏輯分離。 為了實現這一點,我構建了一個單獨的UiController服務來處理用戶交互和演示。 每當需要顯示某些內容或需要用戶操作時,與游戲邏輯相關的服務都會向UiController發出請求。

為了盡可能巧妙地實現這一點,我試圖抽象出接口以與UiController交互。 一種常見的交互是選擇,當玩家必須在同一類別的不同選項中選擇一個時使用。 該交互由UiControllerrequestChoice()方法處理,該方法需要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>;
}

這在構建這樣的選擇請求時被證明是有用的,因為optionstype和項目的值被正確預測或拒絕:

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.

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