Vyhledávání na webu

Jak rozšířit generický tuple v TypeScriptu


Snažím se vytvořit funkci, která dekoruje objekty v poli a vrací je. Vrácený typ pro každou položku musí zachovat literály z generik. Existuje způsob, jak to udělat?

type Identified<Id extends string = string> = { id: Id };
type Extended<Id extends string> = Identified<Id> & { test: "test" };

let one: Identified<"one"> = {id:"one"};
let two: Identified<"two"> = {id:"two"};
let three: Identified<"three"> = {id:"three"};

function extendList<A extends Identified>(arr: A[]) {
  return arr.map((item) => ({ ...item, test: "test" }));
}

let extendedList = extendList([one,two,three]); // literal is lost
let oneExtended = extendedList[0]; // this should be {id:"one", test:"test"} (not {id:string...})

Odkaz na hřiště

Odpověď č. 1

Jedním z přístupů je

function extendList<T extends string[]>(
  arr: [...{ [I in keyof T]: Identified<T[I]> }]
) {
  return arr.map((item) => ({ ...item, test: "test" })) as
    { [I in keyof T]: Extended<T[I]> };
}

Myšlenka je udělat extendList() generickou v T, což je n-tice typů doslovného řetězce vlastností id prvků parametru arr. Takže pokud zavoláte extendList([one, two, three]), T by mělo být ["one", "two", "three"].

Pak je typ vstupu arr mapovaný n-tice, která převádí každý prvek T[I] (I-tý prvek n-tice T) na Identified<T[I]>, zatímco výstup je mapovaná n-tice, která převádí každý prvek T[I] na Extended<T[I]>.

Upozorněte, že typ arr není pouze mapovaný typ { [I in keyof T]: Identified<T[I]> }, ale byl zabalen do variadické n-tice ve tvaru [...{ [I in keyof T]: Identified<T[I]> }]; toto je jen proto, aby kompilátor dostal nápovědu, že by měl spíše odhadovat typy n-tic než neuspořádaných polí. Jinak by extendList([one, two, three]) mohl mít T odhadnut jako ("one" | "two" | "three")[], což není to, co chcete.

Vezměte také na vědomí, že jsem musel použít typové tvrzení, abych kompilátoru řekl, že arr.map(item => ({...item, test: "test"})) je požadovaného výstupního typu. Kompilátor to automaticky odvodit nemůže, ani to nemůže ověřit. Je to v podstatě nad schopnosti kompilátoru „vidět“, že arr.map(...) dělá to, co tvrdíte. Podívejte se na tuto odpověď na podobnou otázku pro více informací o tom omezení.

Každopádně, pojďme se ujistit, že se chová tak, jak chcete:

let one: Identified<"one"> = { id: "one" };
let two: Identified<"two"> = { id: "two" };
let three: Identified<"three"> = { id: "three" };
let extendedList = extendList([one, two, three]);
// let extendedList: [Extended<"one">, Extended<"two">, Extended<"three">]
let oneExtended = extendedList[0]; 
// let oneExtended: Extended<"one">

Vypadá to dobře!

Odkaz na kód na playgroundu

Odpověď č. 2

Zkus to

type Identified<Id extends string = string> = { id: Id };
type Extended<Id extends string> = Identified<Id> & { test: "test" };

const one: Identified<"one"> = {id:"one"};
const two: Identified<"two"> = {id:"two"};
const three: Identified<"three"> = {id:"three"};

function extendList<T extends string>(arr: Identified<T>[]): Extended<T>[] {
  return arr.map((item) => ({ ...item, test: "test" }));
}

const extendedList = extendList([one,two,three]);
const oneExtended = extendedList[0];

Související články