簡體   English   中英

TypeScript 接口映射屬性泛型類型

[英]TypeScript interface mapped property generic type

我正在努力使這項工作

interface ObjectPool<Ids, T> {
  pool: {
    [K in Ids]: T<K>;
  };
};

interface Player<Id> {
  id: Id;
}

let playerPool: ObjectPool<0 | 1 | 2, Player>;

以便

playerPool[0].id === 0;
playerPool[1].id === 1;
playerPool[2].id === 2;
// playerPool[3] error

但是 typescript 說我需要一個通用參數在Player中的let playerPool: ObjectPool<0 | 1 | 2, Player>; let playerPool: ObjectPool<0 | 1 | 2, Player>; 所以我試着let playerPool: ObjectPool<0 | 1 | 2, Player<_>>; let playerPool: ObjectPool<0 | 1 | 2, Player<_>>; 但這也行不通

如果您編寫T<K> ,則T需要是某種特定類型的操作(例如, type T<K> =...interface T<K> {...class T<K>... )。 沒有辦法編寫T<K> ,其中T泛型類型參數。 這將需要microsoft/TypeScript#1213中要求的所謂的更高種類的類型,而 TypeScript 對此沒有直接支持。

你可以退后一步,試着想一想你想要做什么,以及是否有任何方法可以在不需要更高種類的類型的情況下表示它。 如果您想要的只是ObjectPool<P, T>擁有P中的所有屬性鍵,並且對於每個這樣的鍵K ,除了T指定的其他一些屬性之外,您希望屬性值具有等於Kid屬性,那么您可以在定義中分離出id部分,以便T只是一個常規類型。 例如:

type ObjectPool<P extends PropertyKey, T> =
  { [K in P]: { id: K } & T };

現在您可以定義Player而不使其通用:

interface Player {
  id: number,  // <-- you don't necessarily need this anymore
  name: string,
}

現在是一個ObjectPool<0 | 1 | 2, Player> ObjectPool<0 | 1 | 2, Player> ObjectPool<0 | 1 | 2, Player>的行為應符合要求:

function processPlayerPool(playerPool: ObjectPool<0 | 1 | 2, Player>) {
  playerPool[0].id === 0;
  playerPool[1].id === 1;
  playerPool[2].id === 2;
  playerPool[2].name;
  playerPool[3] // error
}

然后,您可以定義其他類型來代替Player並使用它們:

interface Wall {
  location: string
  orientation: string
}

function processSmallWallPool(wallPool: ObjectPool<0 | 1, Wall>) {
  wallPool[0].location // okay
  wallPool[0].id === 0; // okay
  wallPool[1].orientation // okay
  wallPool[2] // error
}

您在評論中提到池中有 2,000 個Wall對象。 這是要放入union的很多元素,但可以肯定的是,您可以做到(代碼生成將比試圖說服編譯器計算它更容易):

// console.log("type WallIds = " + Array.from({ length: 2000 }, (_, i) => i).join(" | "));
type WallIds = 0 | 1 | 2 | 3 | 4 | // ✂ SNIP! 
  | 1995 | 1996 | 1997 | 1998 | 1999

然后ObjectPool<WallIds, Wall>也將按需要運行:

function processWallPool(wallPool: ObjectPool<WallIds, Wall>) {
  wallPool[214].location
  wallPool[100].id // 100
  wallPool[1954].orientation
  wallPool[2021] // error
}

請注意,盡管編譯器確實無法對數字文字的聯合進行太多分析。 您可能會遇到比您預期的更多的麻煩。 如果您嘗試使用數字索引i wallPool的元素,編譯器會報錯:

  for (let i = 0; i < 2000; i++) {
    wallPool[i] // error! 
  //~~~~~~~~~~~ 
  // No index signature with a parameter of type 'number' 
  // was found on type 'ObjectPool<WallIds, Wall>'
  }

它不知道i在該循環中被保證是WallIds類型的值。 它推斷number ,並且您不能使用任何舊number索引到wallPool 它必須是WallIds聯合的值。 您可以斷言iWallIds

  for (let i = 0 as WallIds; i < 20000; i++) {
    wallPool[i] // no error, but, 20000 is an oopsie
  }

但是,如上所示,您會遇到編譯器無法理解i++可能使i不再是有效WallIds的問題,如microsoft/TypeScript#14745此評論中所述。

如果您只打算使用數字文字值(如wallPool[123]wallPool[1987] )對WallPool進行索引,那很好。 但是,一旦您開始存儲和操作number類型的索引,您可能會遇到這種方法的障礙。 它對您來說可能仍然值得,但重要的是要意識到這一點。

Playground 代碼鏈接

暫無
暫無

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

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