Fins ara hem vist tipus primitius (string, number, boolean). Però el món real funciona amb objectes.
Usuaris, Productes, Comandes… tots són objectes amb múltiples propietats.
L’Anarquia dels Objectes Anònims
En JavaScript pur, solem passar objectes d’un costat a l’altre confiant en la nostra memòria.
function imprimirUsuari(usuari) {
// 🤞 Esperem que 'usuari' tingui aquestes propietats...
console.log("Nom: " + usuari.name.toUpperCase());
console.log("Edat: " + usuari.age);
}
// Un mes després, algú crida la funció així:
imprimirUsuari({ nom: "Alice", edat: 30 });
// 💥 CRASH: Cannot read properties of undefined (reading 'toUpperCase')
// Per què? Perquè passem "nom" en català però la funció esperava "name" en anglès.function imprimirUsuari(usuari) {
// 🤞 Esperem que 'usuari' tingui aquestes propietats...
console.log("Nom: " + usuari.name.toUpperCase());
console.log("Edat: " + usuari.age);
}
// Un mes després, algú crida la funció així:
imprimirUsuari({ nom: "Alice", edat: 30 });
// 💥 CRASH: Cannot read properties of undefined (reading 'toUpperCase')
// Per què? Perquè passem "nom" en català però la funció esperava "name" en anglès.TypeScript evita això obligant-te a definir la FORMA (Shape) dels teus objectes.
Interfícies: El Contracte
Una interface és com un contracte. Si un objecte diu ser un Usuari, HA_DE complir el contracte.
interface Usuari {
name: string;
age: number;
isDeveloper: boolean;
}
const alice: Usuari = {
name: "Alice",
age: 39,
isDeveloper: true
}; // ✅ Compleix el contracte
const pepe: Usuari = {
name: "Pepe"
}; // ❌ Error: Falten les propietats 'age' i 'isDeveloper'interface Usuari {
name: string;
age: number;
isDeveloper: boolean;
}
const alice: Usuari = {
name: "Alice",
age: 39,
isDeveloper: true
}; // ✅ Compleix el contracte
const pepe: Usuari = {
name: "Pepe"
}; // ❌ Error: Falten les propietats 'age' i 'isDeveloper'Propietats Opcionals (?)
A vegades no tenim totes les dades. Potser l’email és opcional.
Per a això fem servir el signe d’interrogació ? després del nom de la propietat.
interface Producte {
id: number;
nom: string;
descripcio?: string; // 👈 Opcional (string | undefined)
}
const taula: Producte = {
id: 1,
nom: "Taula d'escriptori"
// No cal posar descripció
};interface Producte {
id: number;
nom: string;
descripcio?: string; // 👈 Opcional (string | undefined)
}
const taula: Producte = {
id: 1,
nom: "Taula d'escriptori"
// No cal posar descripció
};Propietats de Només Lectura (readonly)
Si vols assegurar-te que una propietat mai canvi després de crear l’objecte (com un ID), fes servir readonly.
interface Configuracio {
readonly apiKey: string;
theme: "light" | "dark";
}
const config: Configuracio = {
apiKey: "xyz-123",
theme: "light"
};
config.theme = "dark"; // ✅ Pots canviar-ho
config.apiKey = "abc-999"; // ❌ Error: Cannot assign to 'apiKey' because it is a read-only property.interface Configuracio {
readonly apiKey: string;
theme: "light" | "dark";
}
const config: Configuracio = {
apiKey: "xyz-123",
theme: "light"
};
config.theme = "dark"; // ✅ Pots canviar-ho
config.apiKey = "abc-999"; // ❌ Error: Cannot assign to 'apiKey' because it is a read-only property.type vs interface?
Veuràs gent fent servir type Usuari = { ... } en lloc d’interface Usuari { ... }.
Per definir la forma d’un objecte, tots dos funcionen gairebé igual.
Regla d’or (per ara):
- Fes servir
interfaceper definir objectes que representen entitats (Usuari, Post, Producte).- Fes servir
typeper a Unions (string | number), primitius o tuples.
A la Part 8 aprofundirem en les diferències tècniques, però per ara queda’t amb això.
Prova-ho tu!
Anem a posar a prova el que has après. Has de definir una interfície i crear un objecte específic per passar la validació oculta.
Requisits de l’exercici:
- Defineix una interfície anomenada
Videojocamb:titol(string)any(number)plataforma(opcional, string)
- Crea un objecte constant anomenat
jocPreferitque implementi aquesta interfície. - Les dades han de ser exactament: “The Legend of Zelda”, any 1986.
A la Part 4, veurem què passa quan tenim molts objectes iguals… és a dir, Arrays.