In JavaScript, l'operatore Spread (...) è stato introdotto per facilitare la copia e l'estensione degli oggetti, utilizzato a lungo con gli array e, a partire da ES2018, anche con gli oggetti. In TypeScript, lo Spread è diventato non solo un miglioramento sintattico, ma anche uno strumento per una tipizzazione accurata nella manipolazione degli oggetti.
Storicamente, la clonazione o l'estensione di un oggetto in JavaScript veniva effettuata tramite Object.assign, ma questo approccio portava facilmente alla perdita di sicurezza tipologica e a pericolosi incroci di chiavi, soprattutto se la struttura dell'oggetto era complessa.
Il problema è che utilizzando lo Spread per unire/estendere oggetti, TypeScript inferisce un nuovo tipo basato sulle strutture in ingresso, gestendo i possibili conflitti di chiavi ("l'ultimo prevale"), ma questo non è sempre ciò che il programmatore intendeva. Un'attenzione particolare deve essere riservata agli incroci con campi opzionali, readonly e alla presenza di proprietà private nelle classi.
Soluzione: utilizzare lo Spread, consentendo al compilatore TypeScript di inferire automaticamente il risultato. Per casi complessi, è importante limitare esplicitamente il tipo delle strutture in ingresso e prestare attenzione alle modifiche della struttura dei tipi.
Esempio di codice:
interface User { name: string; age: number; } interface Extra { isAdmin?: boolean; readonly city: string; } const base: User = { name: 'Ivan', age: 28 }; const extended: User & Extra = { ...base, city: 'Moscow', isAdmin: true };
Caratteristiche chiave:
È possibile ottenere una "deep copy" di un oggetto in TypeScript utilizzando lo Spread?
Risposta: No. Lo Spread effettua solo una copia superficiale (shallow copy), gli oggetti annidati rimangono per riferimento.
const original = { user: { name: 'Anna' } }; const cloned = { ...original }; cloned.user.name = 'Maria'; // original.user.name cambierà anch'esso
Il tipo si restringerà o si espanderà durante lo Spread di oggetti con chiavi sovrapposte, e come influisce sull'annotazione della variabile?
Risposta: Durante lo Spread, il tipo della variabile si espande, e in caso di chiavi sovrapposte, prevalgono le proprietà a destra; se è presente un'annotazione esplicita del tipo, potrebbe comparire un errore se i tipi non sono compatibili.
const a = { id: 4, value: "abc" }; const b = { value: 123 }; const c: { id: number; value: number } = { ...a, ...b }; // ok
Può lo Spread essere applicato alle classi e cosa succede con le proprietà private?
Risposta: Lo Spread è applicabile solo alle proprietà pubbliche della classe. Le proprietà private (private/#) e protette (protected) non entreranno nell'oggetto risultante.
class Person { private id = 77; name = "Bob"; } const p = new Person(); const spreaded = { ...p }; // spreaded: { name: string }
Lo sviluppatore utilizza lo Spread dei valori dell'oggetto settings per estendere le opzioni utente:
const common = { theme: 'light', notifications: true }; const user = { notifications: false, signature: 'Sasha' }; const merged = { ...common, ...user };
Si aspetta che merged sia coerente per tipo, ma commette per caso un errore con il tipo del campo signature, dimenticando che lo Spread copia semplicemente le chiavi, senza validare i loro valori.
Vantaggi:
Svantaggi:
Per unire le configurazioni, si utilizza lo Spread solo dopo la validazione e con annotazione del tipo restituito:
interface Settings { theme: "light" | "dark"; notifications: boolean; signature?: string; } function getSettings(common: Settings, specific: Partial<Settings>): Settings { return { ...common, ...specific }; }
Vantaggi:
Svantaggi: