Saltar al contingut principal
Tornar enrere

TypeScript #9: Generics Intermedis (Constraints)

#typescript #generics #constraints

Com domar els Generics perquè no acceptin "qualsevol cosa". Constraints, valors per defecte i el truc de keyof.

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ó:

typescript
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).

typescript
// 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

Generics 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:

  1. T: L’objecte.
  2. K: La clau (key). Però K no pot ser qualsevol string, ha de ser una key de T.
typescript
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 developer

Valors 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.

typescript
// 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

Provem-ho!

Creem una funció fusionar que prengui dos objectes obj1 i obj2 i els combini. Tipem la funció usant dos generics T i U.