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];
