A la part anterior vam veure que <T> pot ser qualsevol cosa. A vegades, això és massa flexible.
Què passa si volem que T tingui sí o sí una propietat .length?
Restriccions (extends)
Imaginem aquesta funció:
function getLength<T>(item: T) {
return item.length; // ❌ Error: Property 'length' does not exist on type 'T'.
}
// TS es queixa perquè T podria ser un número (que no té .length),
// o un boolean...function getLength<T>(item: T) {
return item.length; // ❌ Error: Property 'length' does not exist on type 'T'.
}
// TS es queixa perquè T podria ser un número (que no té .length),
// o un boolean...Per arreglar-ho, usem extends per posar una restricció (Constraint).
// Li diem: "T pot ser el que vulguis, PERÒ ha d'estendre (complir) aquesta forma"
interface TeLongitud {
length: number;
}
function getLength<T extends TeLongitud>(item: T) {
return item.length; // ✅ Ara TS sap que existeix
}
getLength("Hola"); // ✅ string té .length
getLength([1, 2, 3]); // ✅ array té .length
getLength(123); // ❌ Error: number no té .length// Li diem: "T pot ser el que vulguis, PERÒ ha d'estendre (complir) aquesta forma"
interface TeLongitud {
length: number;
}
function getLength<T extends TeLongitud>(item: T) {
return item.length; // ✅ Ara TS sap que existeix
}
getLength("Hola"); // ✅ string té .length
getLength([1, 2, 3]); // ✅ array té .length
getLength(123); // ❌ Error: number no té .lengthGenerics amb keyof (El truc mestre)
Aquest patró el veurem a tot arreu. Volem una funció que obtingui una propietat d’un objecte de forma segura.
Necessitem definir dos Generics:
T: L’objecte.K: La clau (key). PeròKno pot ser qualsevol string, ha de ser una key de T.
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const developer = {
name: "AliceDev",
language: "TypeScript"
};
getProperty(developer, "name"); // ✅ TS sap que això retorna string
getProperty(developer, "age"); // ❌ Error: "age" no és una key de developerfunction getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const developer = {
name: "AliceDev",
language: "TypeScript"
};
getProperty(developer, "name"); // ✅ TS sap que això retorna string
getProperty(developer, "age"); // ❌ Error: "age" no és una key de developerValors per Defecte (=)
Igual que en les funcions normals (function suma(a, b = 0)), els Generics poden tenir valors per defecte.
Això és molt útil per no obligar l’usuari a escriure el tipus sempre.
// Si no passem T, assumiré que és 'string'
interface RespostaAPI<T = string> {
data: T;
status: number;
}
// Ús sense arguments:
const simple: RespostaAPI = { data: "Hola", status: 200 }; // T és string
// Ús amb arguments:
const complex: RespostaAPI<number> = { data: 123, status: 200 }; // T és number// Si no passem T, assumiré que és 'string'
interface RespostaAPI<T = string> {
data: T;
status: number;
}
// Ús sense arguments:
const simple: RespostaAPI = { data: "Hola", status: 200 }; // T és string
// Ús amb arguments:
const complex: RespostaAPI<number> = { data: 123, status: 200 }; // T és numberProvem-ho!
Creem una funció fusionar que prengui dos objectes obj1 i obj2 i els combini. Tipem la funció usant dos generics T i U.