0Pricing
TypeScript Academy · Lezione

Pattern di immutabilità profonda

Ottenga strutture profondamente readonly con tipi ricorsivi.

Pattern di immutabilità profonda è una lezione TypeScript Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento TypeScript Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso TypeScript Academy include 4 lezioni in totale.

Immutabilità superficiale e profonda

Il modificatore readonly è superficiale: protegge il livello superiore, ma non gli oggetti annidati. L'immutabilità profonda significa che ogni livello, fino in fondo, è readonly. Per ottenerla è necessario del lavoro aggiuntivo.

interface State { readonly user: { name: string }; }
const s: State = { user: { name: 'Ada' } };
// s.user = {...}; // Error (shallow protection)
s.user.name = 'Grace'; // allowed! nested field is mutable
console.log(s.user.name);

Perché la protezione superficiale non basta

Per la gestione dello stato e dei dati condivisi, readonly superficiale lascia esposti i campi annidati. Una singola mutazione sfuggita in profondità in una struttura può causare bug sottili e difficili da ricondurre all'origine.

interface Config { readonly db: { host: string; port: number }; }
const c: Config = { db: { host: 'localhost', port: 5432 } };
c.db.port = 9999; // mutates nested data despite readonly
console.log(c.db.port);

Un tipo DeepReadonly ricorsivo

Possiamo creare un mapped type che applichi readonly in modo ricorsivo. DeepReadonly<T> attraversa ogni proprietà e, se è un oggetto, ricorre al livello successivo.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
console.log('DeepReadonly defined');

Applicare DeepReadonly

Racchiudere un tipo in DeepReadonly rende immutabili anche le proprietà annidate. Ora non è possibile riassegnare neppure s.user.name: la protezione arriva fino in fondo.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
type State = DeepReadonly<{ user: { name: string } }>;
const s: State = { user: { name: 'Ada' } };
// s.user.name = 'x'; // Error now
console.log(s.user.name);

Come funziona il mapped type

[K in keyof T] itera su ogni chiave. Il prefisso readonly blocca ciascuna di esse. La condizione T[K] extends object ? ... : T[K] decide se ricorrere o fermarsi a un tipo primitivo.

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
type Cfg = DeepReadonly<{ a: number; b: { c: string } }>;
const cfg: Cfg = { a: 1, b: { c: 'x' } };
console.log(cfg.a, cfg.b.c);

Protezione solo in fase di compilazione

DeepReadonly impone l'immutabilità nel sistema dei tipi, non durante l'esecuzione. L'oggetto JavaScript è ancora tecnicamente modificabile attraverso percorsi privi di tipi, ma il codice tipizzato è completamente protetto.

// Type-level immutability does not freeze the runtime object.
// For runtime guarantees, combine it with Object.freeze.
console.log('Types guard your code; freeze guards runtime');

Congelare gli oggetti annidati durante l'esecuzione

Per ottenere l'immutabilità effettiva durante l'esecuzione, utilizzi Object.freeze. Impedisce di aggiungere, rimuovere o modificare proprietà, ma, come readonly, per impostazione predefinita agisce solo superficialmente.

const config = Object.freeze({ host: 'localhost', port: 8080 });
console.log(config.port);
// In strict mode, config.port = 1 throws; otherwise silently ignored.

Un helper per il congelamento profondo

Per congelare in profondità durante l'esecuzione, ricorra attraverso gli oggetti annidati e congeli ciascuno di essi. Questo si abbina naturalmente a DeepReadonly a livello di tipo, per ottenere un'immutabilità completa.

function deepFreeze<T>(obj: T): T {
  Object.values(obj as any).forEach((v) => {
    if (v && typeof v === 'object') deepFreeze(v);
  });
  return Object.freeze(obj);
}
const frozen = deepFreeze({ a: { b: 1 } });
console.log(frozen.a.b);

Immutabilità per uno stato più sicuro

L'immutabilità profonda è alla base di uno stato prevedibile. Quando i dati non possono essere modificati sul posto, gli aggiornamenti devono creare nuovi oggetti, rendendo le modifiche esplicite e facili da monitorare.

const state = { count: 0, user: { name: 'Ada' } };
// Immutable update: create a new object instead of mutating
const next = { ...state, count: state.count + 1 };
console.log(state.count, next.count);

Modello degli aggiornamenti immutabili

Con i dati readonly, produce nuove versioni usando lo spread. Ogni aggiornamento crea un nuovo oggetto, quindi gli stati precedenti rimangono intatti: una caratteristica preziosa per l'annullamento, il debug tramite viaggio nel tempo e il rilevamento delle modifiche.

type State = { readonly items: readonly string[] };
const s: State = { items: ['a'] };
const updated: State = { items: [...s.items, 'b'] };
console.log(s.items, updated.items);

Combinare sicurezza dei tipi e dell'esecuzione

L'approccio più solido utilizza entrambi: DeepReadonly per le garanzie in fase di compilazione e deepFreeze per l'applicazione durante l'esecuzione. Insieme rendono quasi impossibili le mutazioni accidentali.

function freeze<T>(obj: T): T {
  return Object.freeze(obj);
}
const settings = freeze({ theme: 'dark', version: 2 });
console.log(settings.theme, settings.version);

Verifica rapida

Verifichi la Sua comprensione dell'immutabilità profonda.

Riepilogo: immutabilità profonda

Ha imparato che:

  • readonly è superficiale: i campi annidati restano modificabili.
  • Un mapped type ricorsivo DeepReadonly<T> applica readonly a ogni livello.
  • Esiste solo in fase di compilazione; utilizzi Object.freeze in profondità per imporre l'immutabilità durante l'esecuzione.
  • L'immutabilità consente di gestire lo stato in modo sicuro e prevedibile tramite modelli di copia e aggiornamento.

Con questo si conclude il percorso di TypeScript Academy sui tipi avanzati e l'immutabilità.

type State = { readonly value: number };
const s: State = { value: 1 };
const next: State = { ...s, value: s.value + 1 };
console.log(s.value, next.value);

Domande Frequenti

La lezione «Pattern di immutabilità profonda» è gratuita?

Sì — il testo completo di «Pattern di immutabilità profonda» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso TypeScript Academy, passa a CoddyKit PRO. Il corso TypeScript Academy include 4 lezioni in totale.

Cosa imparerò in «Pattern di immutabilità profonda»?

Ottenga strutture profondamente readonly con tipi ricorsivi. Eserciti TypeScript Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare TypeScript Academy?

Non è richiesta alcuna esperienza precedente. TypeScript Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Pattern di immutabilità profonda»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione TypeScript Academy?

Sì. Ogni lezione TypeScript Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Proprietà readonly
  2. Array e tuple readonly
  3. ReadonlyArray e ReadonlyMap
  4. Pattern di immutabilità profonda
← Torna a TypeScript Academy