Aquí és on molta gent es perd. Comencem a veure <T>, interface, type, return T… tot barrejat i sembla jeroglífic.
Anem a desenredar-ho pas a pas.
Generics en Interfaces vs Tipus
Quan usar un o l’altre? La sintaxi és gairebé idèntica.
// Opció A: Interface Genèrica
interface CaixaInterface<T> {
valor: T;
}
// Opció B: Type Genèric
type CaixaType<T> = {
valor: T;
}
// S'usen IGUAL
const a: CaixaInterface<string> = { valor: "Hola" };
const b: CaixaType<string> = { valor: "Hola" };// Opció A: Interface Genèrica
interface CaixaInterface<T> {
valor: T;
}
// Opció B: Type Genèric
type CaixaType<T> = {
valor: T;
}
// S'usen IGUAL
const a: CaixaInterface<string> = { valor: "Hola" };
const b: CaixaType<string> = { valor: "Hola" };No hi ha diferència funcional real per a dades simples. Usem la que preferim (solem usar interface per a objectes i type per a funcions/unions).
El Patró “Wrapper” (Datawrapper)
Aquest és el cas d’ús #1 en el món real: Respostes d’API.
Tenim una estructura fixa (data, status, error) però el contingut de data canvia segons la crida.
// Definim la "closca" genèrica
interface ApiResponse<Data> {
status: number;
message: string;
data: Data; // 👈 Aquí va la màgia
}
// Definim els nostres models concrets
interface Usuari { id: number; name: string; }
interface Producte { sku: string; price: number; }
// Combinem-los!
type UserResponse = ApiResponse<Usuari>;
type ProductResponse = ApiResponse<Producte>;
function fetchUser(): UserResponse {
return {
status: 200,
message: "OK",
data: { id: 1, name: "AliceDev" } // TS sap que data és Usuari
};
}// Definim la "closca" genèrica
interface ApiResponse<Data> {
status: number;
message: string;
data: Data; // 👈 Aquí va la màgia
}
// Definim els nostres models concrets
interface Usuari { id: number; name: string; }
interface Producte { sku: string; price: number; }
// Combinem-los!
type UserResponse = ApiResponse<Usuari>;
type ProductResponse = ApiResponse<Producte>;
function fetchUser(): UserResponse {
return {
status: 200,
message: "OK",
data: { id: 1, name: "AliceDev" } // TS sap que data és Usuari
};
}”Prop Drilling” de Tipus
A vegades tenim Generics dins de Generics. És com passar una variable d’un avi a un net.
interface PaginatedResponse<T> {
items: T[]; // Array de T
total: number;
page: number;
}
// ApiResponse conté PaginatedResponse, que conté Usuari
type UsersApi = ApiResponse<PaginatedResponse<Usuari>>;
// Sembla complex, però TS ho resol sol:
// UsersApi = {
// status: number,
// data: {
// items: Usuari[],
// total: number
// }
// }interface PaginatedResponse<T> {
items: T[]; // Array de T
total: number;
page: number;
}
// ApiResponse conté PaginatedResponse, que conté Usuari
type UsersApi = ApiResponse<PaginatedResponse<Usuari>>;
// Sembla complex, però TS ho resol sol:
// UsersApi = {
// status: number,
// data: {
// items: Usuari[],
// total: number
// }
// }Avançat: infer (Funcions de Tipus)
Això ja és nivell cinturó negre, però només perquè sàpigues que existeix.
Pots crear un Tipus que “extregui” tipus d’altres tipus usant infer.
// Un tipus que extreu el tipus de dins d'un Array
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type StringArray = string[];
type JustString = UnpackArray<StringArray>;
// Resultat: string// Un tipus que extreu el tipus de dins d'un Array
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type StringArray = string[];
type JustString = UnpackArray<StringArray>;
// Resultat: stringInsight: Funcions de Tipus
T’adones del que acabem de fer? UnpackArray<T> és literalment una funció.
Rep un argument (T).
Fa un if (extends).
Retorna un resultat (U o T).
El sistema de tipus de TypeScript ÉS un llenguatge de programació en si mateix. 🤯
Per què UnpackArray retorna string
Explicació detallada de infer U:
Quan escrivim T extends (infer U)[], estem preguntant a TS: “El tipus T s’assembla a un Array d’alguna cosa? Si és així, captura aquesta ‘cosa’ i digues-li U”.
- Passem
string[]com aT. - TS compara
string[]contra(infer U)[]. - Dedueix que
Uha de serstring. - Retorna
U(string).
Et toca!
Defineix dues variables usant la interfície ApiResponse que acabem de veure a dalt.
acierto: Ha de ser una resposta correcta ambdatasent unnumber(ex: 42).fallo: Ha de ser una resposta ambdatasentstring(ex: “Error greu”).