Mutabile e Immutabile in TypeScript

Mutabile e Immutabile in TypeScript

Questo articolo spiega i concetti di mutabilità e immutabilità in TypeScript.

YouTube Video

Mutabile e Immutabile in TypeScript

Cosa significa Mutabile?

Mutabile significa che un valore può essere modificato. Tipi di riferimento come oggetti e array sono esempi tipici di strutture dati mutabili.

Esempio di Oggetto Mutabile

1type Person = { name: string; age: number };
2
3// Mutable Example: Object
4let person: Person = { name: "Alice", age: 25 };
5person.age = 26;
6console.log(person); // { name: "Alice", age: 26 }

In questo codice, la proprietà age dell'oggetto person viene modificata da 25 a 26. Poiché gli oggetti sono passati per riferimento, il contenuto all'indirizzo di memoria memorizzato nella variabile person viene modificato.

Esempio di Array Mutabile

1// Mutable Example: Array
2let numbers: number[] = [1, 2, 3];
3numbers.push(4);
4console.log(numbers); // [1, 2, 3, 4]

In questo codice, il metodo push viene utilizzato per aggiungere un nuovo elemento 4 all'array originale. Questo modifica l'array originale, rendendolo un'operazione mutabile.

Esempio in una Funzione

1// Mutable Example: Function
2function append(arr: number[], value: number): void {
3    arr.push(value); // Modify the original array
4    console.log(arr);
5}
6
7let nums: number[] = [1, 2, 3];
8append(nums, 4);
9console.log(nums); // [1, 2, 3, 4]

Quando si eseguono operazioni mutabili all'interno di una funzione, anche l'array originale viene modificato.

Cosa significa Immutabile?

Immutabile significa che un valore non può essere modificato. I tipi primitivi sono fondamentalmente immutabili.

Esempio di Tipo Primitivo Immutabile

1// Immutable Example: String
2let str: string = "hello";
3str[0] = "H"; // Error: Index assignment is not allowed
4console.log(str); // "hello"

Tentare di cambiare il primo carattere della stringa str in H fallisce perché le stringhe sono immutabili.

Esempio in una Funzione

1// Immutable Example: Function
2function increment(num: number): number {
3    num++; // This modifies only the local copy of num
4    return num;
5}
6
7let number: number = 10;
8console.log(increment(number)); // 11
9console.log(number); // 10 (original number remains unchanged)

Poiché i numeri sono immutabili, le operazioni all'interno di una funzione non influenzano la variabile originale.

Operazioni Immutabili su Array

Gli array sono mutabili, ma creando un nuovo array invece di modificare quello originale, si possono ottenere operazioni immutabili.

1// Create an array of numbers
2let numbers: number[] = [1, 2, 3];
3
4// Immutable Example: Creating a new array
5let newNumbers: number[] = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

Qui, la sintassi spread (...) viene utilizzata per creare un nuovo array newNumbers. Poiché l'array originale numbers rimane invariato, questa è un'operazione immutabile.

Vantaggi dell'Utilizzo di Strutture Dati Immutabili

Maggiore Predicibilità

I dati immutabili non cambiano, riducendo la probabilità di modifiche inattese e il rischio di bug.

Compatibilità con Librerie Basate sull'Immutabilità

Librerie come React e Redux sono spesso progettate basandosi su dati immutabili, facilitando la gestione dello stato quando usate in modo appropriato.

Rendere gli Oggetti Immutabili con Object.freeze

Object.freeze può essere utilizzato per impedire modifiche a un oggetto.

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

Tuttavia, Object.freeze esegue solo un blocco superficiale, il che significa che le proprietà degli oggetti annidati rimangono mutabili.

1// Create a frozen object with a nested object
2const user: Readonly<{ profile: { name: string } }> = Object.freeze({
3    profile: { name: "Bob" }
4});
5
6// Attempt to modify a nested property (this works because Object.freeze() is shallow)
7user.profile.name = "Charlie"; // No TypeScript error, but still mutable
8
9console.log(user.profile.name); // "Charlie" (nested object is still mutable)

Per creare un oggetto completamente immutabile, è necessario un blocco completo.

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze<T>(obj: T): Readonly<T> {
 3    Object.freeze(obj);
 4    Object.getOwnPropertyNames(obj).forEach(prop => {
 5        const value = (obj as any)[prop];
 6        if (typeof value === "object" && value !== null) {
 7            deepFreeze(value);
 8        }
 9    });
10    return obj;
11}
12
13// Create a deeply frozen object
14const user = deepFreeze({
15  profile: { name: "Bob" }
16});
17
18// Attempt to modify a nested property
19// (this will now throw an error in strict mode)
20user.profile.name = "Charlie";  // No TypeScript error, but modification is not allowed at runtime
21
22console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

Riepilogo

  • I dati mutabili sono modificabili e includono oggetti e array.
  • I dati immutabili non sono modificabili e includono tipi primitivi come stringhe e numeri.
  • Con l'uso della sintassi spread o di metodi come map, è possibile eseguire operazioni su dati immutabili.
  • Object.freeze e deepFreeze possono essere utilizzati per impedire modifiche agli oggetti.
  • L'uso di dati immutabili aiuta a scrivere codice più prevedibile e meno soggetto a errori.

Adottare un design immutabile migliora la sicurezza e la leggibilità del codice, quindi sfruttalo al massimo!

Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.

YouTube Video